home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 26
/
Cream of the Crop 26.iso
/
os2
/
ihpfs124.zip
/
ihpfs.asm
< prev
next >
Wrap
Assembly Source File
|
1997-06-01
|
121KB
|
4,646 lines
;
; iHPFS - An Installable HPFS Driver for DOS
; Copyright (C) 1993-1997 Marcus Better
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;
;==================================================================
; This program is written for Turbo Assembler 3.2, and uses ideal
; mode syntax. I use the following commands to build iHPFS:
; tasm /la /zn /t /m ihpfs.asm
; tlink ihpfs.obj
;==================================================================
IDEAL
P386
JUMPS
LOCALS
;------------------------------------------------- Constants
; Version
VerMajor EQU 1
VerMinor EQU 24
; Errors
errFileNotFound EQU 02h
errPathNotFound EQU 03h
errTooManyFiles EQU 04h
errAccessDenied EQU 05h
errInvalidHandle EQU 06h
errNoMoreFiles EQU 12h
errWriteProt EQU 13h
errSectorNotFound EQU 1Bh
TimeZone EQU -3600
MinCacheSize EQU 32 ; Minimum cache size in KB
MaxCacheSize EQU 32768 ; Maximum cache size in KB
LoadFactor EQU 5 ; Elements/chain in hash.
CacheEntrySize EQU 14 ; Cache entry size
MaxPathLength EQU 57 ; Maximum length of pathname in DOS
; Disk access methods
Method_CHS EQU 0 ; CHS addressing
Method_CHSExt EQU 1 ; Extended CHS addressing
Method_Ext EQU 2 ; IBM/MS Extensions
;------------------------------------------------- Macros
MACRO Abort Code
mov bx, Code
call AbortMsg
ENDM
MACRO NOASSUME
ASSUME cs:NOTHING,ds:NOTHING,es:NOTHING,fs:NOTHING,gs:NOTHING,ss:NOTHING
ENDM
MACRO DEFASSUME
ASSUME cs:ResCode,ds:ResCode,es:NOTHING,fs:NOTHING,gs:ResData,ss:NOTHING
ENDM
;------------------------------------------------- Structures
STRUC XMSMoveStruct
Length DD 0 ; Number of bytes to transfer
SourceHandle DW 0 ; Handle of source block
SourceOffset DD 0 ; Offset into source block
DestHandle DW 0 ; Handle of dest block
DestOffset DD 0 ; Offset into dest block
ENDS XMSMoveStruct
STRUC DiskAddrPacketStruct ; IBM/MS Extensions disk address packet
Length DB 10h ; Length of packet
DB 0
Count DW 0 ; Number of blocks to transfer
Buffer DD 0 ; Address of transfer buffer
Sector DQ 0 ; Starting absolute sector number
ENDS DiskAddrPacketStruct
;------------------------------------------------- Procedures
PROCDESC DiskRead PASCAL NEAR :DWORD,:WORD,:DWORD
PROCDESC ReadSector PASCAL NEAR :DWORD,:WORD,:DWORD
PROCDESC ReadFileSector PASCAL NEAR :DWORD,:WORD,:DWORD,:DWORD
PROCDESC ChkPathLength PASCAL NEAR :DWORD
PROCDESC ScanPartTbl PASCAL NEAR :WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:DWORD
PROCDESC CheckHPFSPart PASCAL NEAR :WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:DWORD
PROCDESC ReadSectorExtCHS PASCAL NEAR :DWORD,:WORD,:WORD,:WORD,:DWORD
PROCDESC CheckCylNumber PASCAL NEAR :DWORD,:WORD,:WORD
PROCDESC IsInstalledPart PASCAL NEAR :WORD
PROCDESC QueryPart PASCAL NEAR :WORD,:WORD
PROCDESC RemoveDrv PASCAL NEAR :WORD,:WORD
PROCDESC QueryDrive PASCAL NEAR :WORD,:WORD
PROCDESC UninstallDriver PASCAL NEAR :WORD
;======================================================= Resident section
SEGMENT ResCode
ASSUME cs:ResCode,ds:NOTHING,es:NOTHING,ss:NOTHING,fs:NOTHING,gs:NOTHING
;-------------------------------------------------- INT 2D entry
; Follows the Alternate Multiplex Interrupt Specification (AMIS) [v3.5.1]
; This is an IBM interrupt sharing protocol entry point.
; The entry point is located in the beginning of the segment,
; so that the rest of the segment may be released on uninstall.
Int2DEntry:
jmp SHORT JmpToInt2DHandler
OldInt2D DD 0 ; Saved vector for next handler in chain
DW 424Bh ; Protocol signature
DB 00h ; EOI flag - software interrupt
jmp SHORT HardwareReset2D ; Hardware reset routine
DB 7 DUP (0) ; Reserved
JmpToInt2DHandler:
jmp Int2DHandler
HardwareReset2D:
retf
;-------------------------------------------------- INT 2F entry
; This is an IBM interrupt sharing protocol entry point.
Int2FEntry:
jmp SHORT JmpToInt2FHandler
OldInt2F DD 0 ; Saved vector for next handler in chain
DW 424Bh ; Protocol signature
DB 00h ; EOI flag - software interrupt.
jmp SHORT HardwareReset2F ; Hardware reset routine
DB 7 DUP (0) ; Reserved
JmpToInt2FHandler:
jmp Int2FHandler
HardwareReset2F:
retf
EndUninstalledCode: ; Last byte to keep when uninstalled
;-------------------------------------------------- Common resident data
DataSegs DW 26 DUP(0) ; Data segments for the drives
; AMIS information
AMISSign DB "M Better" ; Manufacturer name
DB "iHPFS " ; Product name
DB "HPFS Driver for DOS", 0 ; Description, ASCIIZ.
HookList DB 2Fh
DW OFFSET Int2FEntry
DB 2Dh
DW OFFSET Int2DEntry
ApiFunc DB 0 ; INT 2D function # for API.
; Saved registers and vectors
FuncAddr DW 0 ; Pointer to current redirector function
ResDataSeg DW 0 ; Current resident data segment
DriveNo DB 0 ; Current drive number
SaveSP DW 0 ; SP on entry to interrupt handler
SaveSS DW 0 ; SS on entry to interrupt handler
Novell DB 0 ; Set if Novell DOS
; Pointers to SDA fields. Layout:
; DOS4+ DOS 3, DR-DOS
; DTA ptr 0Ch 0Ch
; First filename buffer 9Eh 92h
; Search data block 19Eh 192h
; Dir entry for found file 1B3h 1A7h
; Search attributes 24Dh 23Ah
; File access/sharing mode 24Eh 23Bh
; Ptr to current CDS 282h 26Ch
; Extended open mode 2E1h Not supported
SDA DD 0 ; Address of DOS Swappable Data Area
PSP DW 0 ; Program Segment Prefix
pCurrCDS DD 282h ; Pointer to current CDS
pDTA DD 0Ch ; Pointer to current DTA
FN1 DD 9Eh ; Address of first filename field
AccMode DD 24Eh ; Address of file access/sharing mode field
SrchAttr DD 24Dh ; Address of search attributes
ExtOpenMode DD 2E1h ; Address of extended open mode
; Buffers.
Buf1 DB 512 DUP(0)
Buf2 DB 512 DUP(0)
Buf3 DB 512 DUP(0)
Buf4 DB 512 DUP(0)
FNameBuf DB 128 DUP(0)
FNameBuf2 DB 128 DUP(0)
BufUsed DB 0 ; Flag for FindNext, set if buffers untouched.
; Some flags
Multitrack DB 1 ; Allow multitrack reads and writes.
; Characters permitted in filenames, etc
MinPerm DB 0 ; Lowest permissible character
MaxPerm DB 255 ; Highest permissible character
MinExcl DB 0 ; Lowest excluded character
MaxExcl DB 0 ; Highest excluded character
NumTerm DB 0 ; Number of illegal (terminator) characters
TermChars DB 32 DUP (0) ; Array of illegal characters
UpCaseTbl DB 128 DUP (0) ; File Character Upper-Case Table
DaysInMonth DB 31,28,31,30,31,30,31,31,30,31,30,31
ConvertLong DB 0 ; Convert long filenames flag
ConvTable DB "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_!#%"
; Cache info
CacheOn DB 0 ; Flag: Caching active?
XMSEntry DD 0 ; XMS driver entry point
hHashTable DW 0 ; XMS handle to the hash table
hCacheLists DW 0 ; XMS handle to the hash chains
hCacheSectors DW 0 ; XMS handle to the cache sectors
CacheEntries DW 0 ; Entries in the cache
HashSize DW 0 ; Slots in the hash table
FreeEntry DW 0 ; Next free entry pointer
XMoveStruc XMSMoveStruct <>
XMSError DB 0 ; Set if XMS call failed
EntryBuf DB 20 DUP (0)
; IBM/MS Extensions data
DiskAddrPkt DiskAddrPacketStruct <>
; Jump Table, 0 means unsupported
JmpTbl DW 0, RmDir, 0, MkDir
DW 0, ChDir, Close, Commit
DW Read, Write, LockRegion, UnlockRegion
DW DiskFree, 0, SetAttrib, GetAttrib
DW 0, Rename, 0, Delete
DW 0, 0, Open, Create
DW 0, 0, 0, FindFirst
DW FindNext, 0, 0, 0
DW 0, Seek, 0, 0
DW 0, 0, 0, 0
DW 0, 0, 0, 0
DW 0, 0, ExtOpen
;------------------------------------------------------------------------
; Int 2D (Alternate Multiplex) handler.
PROC Int2DHandler FAR
NOASSUME
ASSUME cs:ResCode
cmp ah, [ApiFunc]
je apiApiCall
jmp [OldInt2D]
apiApiCall:
or al, al ; Installation check
je apiInstallChk
cmp al, 2 ; Uninstall
je apiUnInstall
cmp al, 4 ; Determine chained interrupts
je apiGetHookList
cmp al, 10h ; Query drive installed
je apiQueryDrive
xor al, al ; Not implemented
iret
; iHPFS installation check.
apiInstallChk:
mov al, 0FFh ; Multiplex number in use
mov ch, VerMajor
mov cl, VerMinor
mov dx, cs
mov di, OFFSET AMISSign ; DX:DI -> AMIS signature
iret
apiGetHookList:
mov dx, cs
mov bx, OFFSET HookList
mov al, 04h ; Hook list returned.
iret
apiUnInstall:
mov bx, ResCode
mov al, 03h
iret
; Query drive installed. Drive number in BX (will be destroyed). Returns
; AH=1 if installed, AH=0 otherwise.
apiQueryDrive:
shl bx, 1
cmp [DataSegs+bx], 0
setnz ah
iret
ENDP Int2DHandler
;------------------------------------------------------------------------
; Int 2F (Multiplex) Interrupt handler.
; Processes calls for function 11h.
PROC Int2FHandler FAR
NOASSUME
ASSUME cs:ResCode
sti
cmp ah, 11h
je Function11
jmp [OldInt2F]
Function11:
; Decide which method to use for determining if the call is for us.
or al, al
jz NetInstallChk ; Redirector installation check
cmp al, 21h ; Seek
je CheckSFT
; cmp al, 1Ch ; Find next
; je CheckFindNext
cmp al, 2Eh
je CheckCDS
cmp al, 1Dh
jbe Function11_1
jmp [OldInt2F]
Function11_1:
cmp al, 06h
jb CheckCDS
cmp al, 0Bh
jna CheckSFT
; CDS method: Check path field of CDS.
CheckCDS:
push ds bx
lds bx, [pCurrCDS]
lds bx, [bx] ; CDS for current file
movzx bx, [BYTE bx]
cmp bl, '\'
je CheckCDS1 ; Not for us - ZF set
sub bl, 'A'
mov [DriveNo], bl
shl bx, 1
mov bx, [DataSegs+bx]
mov [ResDataSeg], bx
or bx, bx ; ZF set if unsupported drive
CheckCDS1:
pop bx ds
jnz CallForUs
jmp [DWORD OldInt2F] ; Call was not for us
; SFT method: Check drive number in SFT entry.
; ES:DI -> SFT entry for file
CheckSFT:
push bx
movzx bx, [BYTE es:di+5]
and bl, 3Fh ; Bits 5-0 contain drive number
mov [DriveNo], bl
shl bx, 1
mov bx, [DataSegs+bx]
mov [ResDataSeg], bx
or bx, bx
pop bx
jnz CallForUs
jmp [DWORD OldInt2F]
; Special check for Find Next function - drive number in SDB.
; Doesn't seem to work with MS-DOS 7 / Windows95.
;CheckFindNext:
; push ds bx
; lds bx, [pDTA]
; lds bx, [bx] ; DS:BX -> DTA
; mov bl, [BYTE es:di] ; SDB drive number
; and bx, 3Fh ; Turn off network bit
; mov [DriveNo], bl
; shl bx, 1
; mov bx, [DataSegs+bx]
; mov [ResDataSeg], bx
; or bx, bx
; pop bx ds
; jnz CallForUs
; jmp [DWORD OldInt2F]
; Call is for our drive
CallForUs:
; Switch stack
mov [cs:SaveSP], sp
mov [cs:SaveSS], ss
push cs
pop ss
mov sp, OFFSET ResStack
ASSUME ss:SEG ResStack
; Transfer to the subfunction handler
push bx
mov bl, al
xor bh, bh
shl bl, 1
mov bx, [JmpTbl+bx]
mov [FuncAddr], bx
pop bx
cmp [FuncAddr], 0
jz Unsupported
push ds gs
push cs
pop ds
mov gs, [ResDataSeg]
DEFASSUME
call [FuncAddr]
pop gs ds
NOASSUME
ASSUME cs:ResCode
lss sp, [DWORD SaveSP]
ret 2
Unsupported:
lss sp, [DWORD SaveSP]
jmp [DWORD OldInt2F] ; Function not supported, ignore it.
; Redirector installed check
NetInstallChk:
mov ax, 00FFh ; Redirector installed.
iret ; Return immediately
ENDP Int2FHandler
;---------------------------------------------------------------------
; Remove Directory
PROC RmDir
stc
mov ax, errPathNotFound
ret
ENDP RmDir
;---------------------------------------------------------------------
; Make Directory
PROC MkDir
stc
mov ax, errWriteProt
ret
ENDP MkDir
;---------------------------------------------------------------------
; Change Directory
PROC ChDir STDCALL
LOCAL @@CDSPointer:DWORD, @@Result
DEFASSUME
pushad
push es
mov [BufUsed], 0
les di, [pCurrCDS]
mov eax, [es:di]
mov [@@CDSPointer], eax
les di, [FN1]
cmp [BYTE es:di+3], 0 ; See if root directory
je @@Root
cmp [BYTE es:di+2], 0 ; See if root in DR-DOS
je @@Root
call FindFile
jc @@PathNotFound
; See if the entry is a subdirectory
test [Buf1+bx+03h], 10h ; Mask out subdirectory attribute
jz @@PathNotFound
call NEAR ChkPathLength PASCAL, [FN1]
jc @@PathNotFound
; Extract the FNode pointer
mov ecx, [DWORD Buf1+bx+04h]
mov [CDFNode], ecx
; Set the CDS directory name to current directory.
lds si, [FN1]
ASSUME ds:NOTHING
les di, [@@CDSPointer]
mov cx, 67 ; Length of CDS path string
cld
@@MovePath:
lodsb
stosb
or al, al
loopnz @@MovePath
clc
jmp @@Done
DEFASSUME
@@Root: mov eax, [RootFNode]
mov [CDFNode], eax
lds bx, [@@CDSPointer]
ASSUME ds:NOTHING
mov [BYTE bx+3], 0 ; Puts 0 after X:\ in CDS Filename
clc
jmp @@Done
@@PathNotFound:
stc
mov [@@Result], errPathNotFound
@@Done:
pop es
popad
jnc @@Exit
mov ax, [@@Result]
@@Exit:
ret
ENDP ChDir
;---------------------------------------------------------------------
; Close File
PROC Close
DEFASSUME
push ax bx
mov ax, 1208h
int 2Fh ; Decrease handle count in SFT
cmp ax, 01h ; Last handle closed?
jne @@1
; Clear the SFT
mov [WORD es:di], 0 ; Number of references
@@1:
pop bx ax
ret
ENDP Close
;---------------------------------------------------------------------
; Commit File
PROC Commit
stc
mov ax, errWriteProt
ret
ENDP Commit
;---------------------------------------------------------------------
; Read from File
PROC Read STDCALL
DEFASSUME
LOCAL @@OrigBytes, @@Bytes, @@RelSect:DWORD, @@SecOfs, @@DTABuf:DWORD
LOCAL @@Result, @@SFTOfs, @@FNode:DWORD
pushad
push es fs
push es
pop fs
ASSUME fs:NOTHING
mov [@@SFTOfs], di
mov [BufUsed], 0
mov [@@Bytes], cx
mov [@@OrigBytes], 0
les bx, [SDA]
les bx, [DWORD es:bx+0Ch] ; DTA Pointer
mov [WORD LOW @@DTABuf], bx
mov [WORD HIGH @@DTABuf], es
mov eax, [fs:di+19h]
mov [@@FNode], eax
push cs
pop es
ASSUME es:ResCode
; Adjust bytes to read if necessary.
movzx eax, [@@Bytes] ; Bytes to read
mov edx, [fs:di+15h] ; File pos
mov ecx, [fs:di+11h] ; File size
cmp edx, ecx
jae @@Succeed ; Beyond EOF
add eax, edx
cmp eax, ecx ; Compare w file size
jna @@1
sub eax, ecx
sub [@@Bytes], ax ; Actual bytes to read
@@1: mov ax, [@@Bytes]
mov [@@OrigBytes], ax
mov eax, edx ; File pos
and dx, 511
mov [@@SecOfs], dx
shr eax, 9
mov [@@RelSect], eax
cmp [@@SecOfs], 0
je @@WholeSectors
; Read sector into Buf2 and copy bytes to DTA
call NEAR ReadFileSector, [@@RelSect], 1, [@@FNode], ds (OFFSET Buf2)
jc @@ReadError
inc [@@RelSect]
mov cx, 512
sub cx, [@@SecOfs] ; Bytes to read from this sector
cmp [@@Bytes], cx
ja @@2
mov cx, [@@Bytes]
@@2:
mov si, OFFSET Buf2
add si, [@@SecOfs]
les di, [@@DTABuf]
ASSUME es:NOTHING
push cx
rep movsb ; Transfer bytes to DTA Buffer
pop cx
add [WORD @@DTABuf], cx ; Increase user buffer offset
sub [@@Bytes], cx ; Decrease bytes left to read
; Read sectors into DTA
@@WholeSectors:
cmp [@@Bytes], 512
jb @@LastSector ; Done if no more bytes
movzx eax, [@@Bytes]
shr ax, 9 ; Sectors to read
call NEAR ReadFileSector, [@@RelSect], ax, [@@FNode], [@@DTABuf]
jc @@ReadError
add [@@RelSect], eax
shl ax, 9 ; Bytes read
add [WORD @@DTABuf], ax
sub [@@Bytes], ax
; Read the last sector into Buf2 and copy part of it to DTA
@@LastSector:
cmp [@@Bytes], 0
jz @@Succeed ; No bytes left to read
call NEAR ReadFileSector, [@@RelSect], 1, [@@FNode], ds (OFFSET Buf2)
jc @@ReadError
mov cx, [@@Bytes]
mov si, OFFSET Buf2
les di, [@@DTABuf]
ASSUME es:NOTHING
rep movsb ; Transfer bytes to user buffer
; Done!
@@Succeed:
movzx ecx, [@@OrigBytes]
mov bx, [@@SFTOfs]
add [fs:bx+15h], ecx
mov [@@Result], cx
clc
jmp @@Done
@@SectorNotFound:
@@ReadError:
mov [@@Result], errSectorNotFound
stc
@@Done:
pop fs es
ASSUME es:NOTHING,fs:NOTHING
popad
jc @@Fail
mov cx, [@@Result]
jmp @@Exit
@@Fail:
mov ax, [@@Result]
@@Exit:
ret
ENDP Read
;---------------------------------------------------------------------
; Write to File
PROC Write STDCALL
stc
mov ax, errWriteProt
ret
ENDP Write
;---------------------------------------------------------------------
; Lock Region of File
PROC LockRegion
clc
ret
ENDP LockRegion
;---------------------------------------------------------------------
; Unlock Region of File
PROC UnlockRegion
clc
ret
ENDP UnlockRegion
;---------------------------------------------------------------------
; Get Disk Space
PROC DiskFree
DEFASSUME
mov ah, [MediaID]
mov al, 1 ; Sectors per cluster
mov ebx, [TotalSectors] ; Number of clusters
xor cl, cl
; Adjust clusters so that value is 16-bit
@@1:
cmp ebx, 0FFFFh ; Cluster count fits in BX?
jbe @@2
shr ebx, 1 ; Divide cluster count by 2
shl al, 1 ; Multiply sectors per cluster by 2
inc cl ; CL=shift value for sector numbers
jmp @@1
@@2:
mov edx, [FreeSectors]
shr edx, cl ; Available clusters
mov cx, 512 ; Bytes/sector
clc
ret
ENDP DiskFree
;---------------------------------------------------------------------
; Set File Attributes
PROC SetAttrib STDCALL
stc
mov ax, errWriteProt
ret
ENDP SetAttrib
;---------------------------------------------------------------------
; Get File Attributes
PROC GetAttrib STDCALL
DEFASSUME
LOCAL @@Result
pushad
push es
mov [BufUsed], 0
les di, [FN1]
call FindFile
jc @@FileNotFound
mov al, [Buf1+bx+03h] ; Extract attributes
and al, 10111111b ; Mask out 8.3 filename bit
xor ah, ah
test al, 10h ; Test if subdir
jz @@SubDirOK
call NEAR ChkPathLength PASCAL, [FN1]
jnc @@SubDirOk
and al, NOT 10h ; Turn off subdir attribute
@@SubDirOk:
mov [@@Result], ax
clc
jmp @@Done
@@FileNotFound:
mov [@@Result], errFileNotFound
stc
@@Done:
pop es
popad
mov ax, [@@Result]
ret
ENDP GetAttrib
;---------------------------------------------------------------------
; Rename File
PROC Rename
stc
mov ax, errWriteProt
ret
ENDP Rename
;---------------------------------------------------------------------
; Delete File
PROC Delete
stc
mov ax, errWriteProt
ret
ENDP Delete
;---------------------------------------------------------------------
; Open File
PROC Open STDCALL
DEFASSUME
LOCAL @@OpenMode:BYTE, @@Result:WORD
pushad
push es fs
mov ax, es
mov fs, ax
mov [BufUsed], 0
mov si, di
les di, [AccMode]
mov al, [es:di] ; File access mode/sharing
cmp [Novell], 0
jnz @@OpenModeOk ; Ignore access mode under Novell DOS
test al, 7
jnz @@errAccessDenied
@@OpenModeOk:
mov [@@OpenMode], al
@@FindFile:
mov di, [WORD LOW FN1] ; First filename buffer
call FindFile
jnc @@Found
mov [@@Result], errFileNotFound
jmp @@Fail
@@Found:
mov al, [Buf1+bx+03h] ; Attributes
and al, 10h ; Test subdir attribute
jnz @@errAccessDenied
; Set SFT fields
mov al, [@@OpenMode]
and al, 7Fh
xor ah, ah
mov [fs:si+02h], ax
mov al, [Buf1+bx+03h] ; Attributes
and al, 10111111b ; Mask out 8.3 filename bit
mov [fs:si+04h], al
mov ax, 8040h ; Device info word
or al, [DriveNo] ; Drive number
mov [fs:si+05h], ax
mov [DWORD fs:si+07h], 0 ; Device driver pointer
mov [WORD fs:si+0Bh], 0 ; Cluster #, local files only
mov eax, [DWORD Buf1+bx+08h]; Timestamp
call Unix2DosTime
mov [fs:si+0Dh], eax
mov eax, [DWORD Buf1+bx+0Ch] ; File size
mov [fs:si+11h], eax
mov [DWORD fs:si+15h], 0 ; Current offset in file
mov eax, [DWORD Buf1+bx+04h]; FNode sector #
mov [fs:si+19h], eax ; Save in REDIRIFS field
; Convert filename to FCB format (11 bytes, no dot, blank-padded)
; Scan to the terminating 0.
cld
xor al, al
mov cx, -1
repne scasb
; Scan back to the last \
std
mov al, '\'
mov cx, -1
repne scasb
add di, 2
xchg si, di
push es
push fs
pop es
pop ds
ASSUME ds:NOTHING
add di, 20h
mov cx, 11
mov al, ' '
cld
rep stosb ; Clear the field
sub di, 11
lea dx, [di+8] ; Extension part
@@Fn1: lodsb
or al, al
je @@Succeed
cmp al, '.'
jne @@Fn2
mov di, dx ; Move on to extension field if '.'
jmp @@Fn1
@@Fn2:
stosb
jmp @@Fn1
@@errAccessDenied:
mov [@@Result], errAccessDenied
jmp @@Fail
@@Succeed:
clc
jmp @@Done
@@Fail: stc
@@Done: pop fs es
popad
jnc @@Exit
mov ax, [@@Result]
@@Exit:
ret
ENDP Open
;---------------------------------------------------------------------
; Create File
PROC Create
stc
mov ax, errWriteProt
ret
ENDP Create
;---------------------------------------------------------------------
; Find First Matching File
PROC FindFirst STDCALL
DEFASSUME
LOCAL @@Result, @@SrchTmplPos, @@DirFNode:DWORD, @@Attr:BYTE
LOCAL @@LongPath:BYTE, @@DTA:DWORD
pushad
push es
mov ax, cs
; Move the filename from SDA First Filename buffer to FNameBuf2
mov [BufUsed], 0
mov [@@LongPath], 0 ; Set if pathname is long
lds si, [pDTA]
ASSUME ds:NOTHING
mov ecx, [si]
mov [@@DTA], ecx ; Address of DTA
lds si, [FN1]
mov es, ax
ASSUME es:ResCode
mov di, OFFSET FNameBuf2
mov cx, 32
cld
rep movsd
mov ds, ax
ASSUME ds:ResCode
; Find the last \ and replace it with 0
xor al, al
mov cx, 128
mov di, OFFSET FNameBuf2
repne scasb
jne @@PathNotFound
dec di
mov al, '\'
sub cx, 128
neg cx
std
repne scasb
jne @@PathNotFound
inc di
mov [BYTE di], 0
inc di
mov [@@SrchTmplPos], di ; Where the search template starts
mov ax, di
sub ax, OFFSET FNameBuf2
cmp ax, MaxPathLength-8 ; Long path?
seta [@@LongPath]
ror [@@LongPath], 1 ; Move flag to high bit
; Find the directory
cmp di, OFFSET FNameBuf2+3 ; See if it's in the root dir.
jne @@FindDir
mov ecx, [RootFNode]
mov [@@DirFNode], ecx
jmp @@DirOk
@@FindDir:
mov di, OFFSET FNameBuf2
call FindFile
jc @@PathNotFound
; Check that it's really a directory.
test [BYTE Buf1+bx+03], 10h
jz @@PathNotFound
mov ecx, [DWORD Buf1+bx+04h] ; FNode of directory
mov [@@DirFNode], ecx
@@DirOk:
les di, [SrchAttr]
ASSUME es:NOTHING
mov al, [es:di] ; Search attribute
mov [@@Attr], al
; Initialize the search data block in the DTA.
; The "drive number" byte does not seem to work as specified in Windows 95.
; We later overwrite this with a magic value that seems to work. The reason
; for this behaviour is unknown.
cld
les di, [@@DTA]
mov al, [DriveNo] ; Drive number
or al, 80h ; Set bit 7 for remote drive
stosb
; Search template - 11 bytes, padded with spaces.
mov cx, 11
mov al, ' '
rep stosb ; Clear the field
sub di, 11
lea dx, [di+8] ; Extension part
mov si, [@@SrchTmplPos]
@@1: lodsb
or al, al
je @@TemplateDone
cmp al, '.'
jne @@2
mov di, dx ; Move on to extension field if '.'
jmp @@1
@@2:
stosb
jmp @@1
@@TemplateDone:
mov di, dx
add di, 3
mov al, [@@Attr]
stosb
; Read the directory FNode
call NEAR ReadSector, [@@DirFNode], 1, ds (OFFSET Buf1)
jc @@NoMoreFiles
mov eax, [DWORD Buf1+48h] ; Starting sector
les bx, [@@DTA]
mov [es:bx+0Dh], eax ; Save in SDB
xor al, al
mov ah, [@@LongPath]
mov [WORD es:bx+11h], ax ; Offset of last entry + long path flag
test [@@Attr], 08h ; Volume label?
jz @@RegularFile
; Volume label
push ds
pop es
ASSUME es:ResCode
mov ds, [ResDataSeg]
ASSUME ds:ResData
; Move volume label to FNameBuf
mov di, OFFSET FNameBuf
mov cx, 8
mov si, OFFSET Volabel
rep movsb
mov al, '.'
stosb
mov cx, 3
rep movsb
push cs
pop ds
ASSUME ds:ResCode
mov dx, OFFSET FNameBuf
mov bx, [@@SrchTmplPos]
call Match
jnc @@DoVolLabel
cmp [@@Attr], 08h ; Only volume label?
je @@NoMoreFiles
jmp @@RegularFile
@@DoVolLabel:
; Set the directory entry for found vol. label.
les di, [@@DTA]
ASSUME es:NOTHING
add di, 15h ; Directory entry for found file
mov cx, 11
mov al, ' '
cld
rep stosb
sub di, 11
lea dx, [di+11]
mov cx, 11
mov si, OFFSET Volabel
mov ds, [ResDataSeg]
ASSUME ds:ResData
@@MoveVolabel:
lodsb
or al, al
jz @@VolabelDone
stosb
loop @@MoveVolabel
@@VolabelDone:
push cs
pop ds
ASSUME ds:ResCode
mov di, dx
mov al, 08h
stosb ; Attributes
add di, 10
xor eax, eax
stosd ; Time and date
stosw ; Starting cluster
stosd ; File size
clc
jmp @@Done
@@RegularFile:
; Call FindNext to do the actual directory search
les di, [@@DTA]
call FindNext
mov [@@Result], ax
jmp @@Done
@@PathNotFound:
stc
mov [@@Result], errPathNotFound
jmp @@Done
@@NoMoreFiles:
stc
mov [@@Result], errNoMoreFiles
jmp @@Done
@@Done:
pop es
popad
jnc @@Exit
mov ax, [@@Result]
@@Exit:
ret
ENDP FindFirst
;---------------------------------------------------------------------
; Find Next Matching File
; Use of reserved or unused SDB fields:
; Offset Size Function
; 0Dh DWORD First sector of the directory block last searched.
; 11h WORD Bit 15 : Set if subdirs are not to be returned (long paths)
; Bits 0-14: Offset into directory block of the last entry
; examined, or 0=no last entry, 1=only "." entry returned.
PROC FindNext STDCALL
DEFASSUME
LOCAL @@Result, @@DirBlock:DWORD, @@LastEntry, @@Root:BYTE, @@NoSubDirs:BYTE
LOCAL @@FileAttr:BYTE, @@FileSize:DWORD, @@DTA:DWORD
pushad
push es
cld
mov [@@Root], 0 ; Flag is set if root dir.
les bx, [pDTA]
mov eax, [es:bx]
mov [@@DTA], eax ; Address of DTA
les bx, [es:bx] ; ES:BX -> DTA
mov eax, [es:bx+0Dh] ; SDB, directory block.
mov [@@DirBlock], eax
mov ax, [es:bx+11h] ; SDB, offset of last entry
mov [@@NoSubDirs], ah ; Copy to subdirectory flag
and [@@NoSubDirs], 80h
and ah, 7Fh
mov [@@LastEntry], ax
@@Search:
cmp [BufUsed], 0
jnz @@GotDirBlock
call NEAR ReadSector, [@@DirBlock], 4, ds (OFFSET Buf1)
jc @@NoMoreFiles
@@GotDirBlock:
mov [BufUsed], 0
; See if we're in the root dir.
mov eax, [DWORD Buf1+0Ch]
cmp eax, [RootFNode]
jne @@TransferTemplate
mov [@@Root], 1 ; Set root dir flag.
; Transfer search template to ASCIIZ format in FNameBuf2
@@TransferTemplate:
push ds
pop es
lds si, [@@DTA]
ASSUME ds:NOTHING, es:ResCode
inc si ; SDB Search template
mov di, OFFSET FNameBuf2
mov cx, 8
@@MoveTmpl1:
lodsb
cmp al, ' ' ; Go to extension if blank
je @@MoveTmplExt
stosb
loop @@MoveTmpl1
inc si
@@MoveTmplExt:
add si, cx ; Extension field in template
dec si
mov cx, 3
mov al, '.'
stosb
@@MoveTmpl2:
lodsb
cmp al, ' '
je @@MoveTmplDone
stosb
loop @@MoveTmpl2
@@MoveTmplDone:
xor al, al ; Zero terminate
stosb
mov ax, cs
mov ds, ax
mov es, ax
ASSUME ds:ResCode, es:NOTHING
mov bx, [@@LastEntry]
cmp bx, 1
ja @@NextEntry
je @@DotDot
mov bx, 14h ; Offset of first dir. entry
jmp @@DoEntry
@@NextEntry:
test [BYTE Buf1+bx+02h], 08h ; Last entry in block?
jz @@MoveToNextEntry
; Read higher level directory block.
call NEAR ReadSector, [DWORD Buf1+0Ch], 4, ds (OFFSET Buf1)
jc @@NoMoreFiles
; Check that it's a directory sector, not the FNode.
cmp [BYTE Buf1+03h], 0F7h ; FNode signature
je @@NoMoreFiles
; Go through this directory block to find the entry that we came from.
mov edx, [@@DirBlock]
mov bx, 14h
@@SrchParent:
test [Buf1+bx+02h], 04h ; Entry has B tree pointer?
jz @@NotParent
mov si, bx
add si, [WORD Buf1+bx]
cmp [DWORD Buf1+si-4], edx
je @@FoundParent
@@NotParent:
test [Buf1+bx+02h], 08h ; Last entry in block?
jnz @@NoMoreFiles
add bx, [WORD Buf1+bx]
jmp @@SrchParent
@@FoundParent:
mov eax, [DWORD Buf1+10h] ; Sector number
mov [@@DirBlock], eax
mov [@@LastEntry], bx
jmp @@NoBTree
@@MoveToNextEntry:
add bx, [WORD Buf1+bx] ; Move to next entry
@@DoEntry:
; Check if entry has a B Tree pointer
mov [@@LastEntry], bx
test [BYTE Buf1+bx+02h], 04h
jz @@NoBTree
; Go down the branch
add bx, [WORD Buf1+bx]
mov eax, [DWORD Buf1+bx-04h] ; B Tree pointer
mov [@@DirBlock], eax
mov [@@LastEntry], 0
jmp @@Search
; No B Tree, check this entry for a match.
@@NoBTree:
test [BYTE Buf1+bx+02h], 08h ; Last entry in block?
jnz @@NextEntry
les di, [@@DTA]
ASSUME es:NOTHING
; Match file attributes with AT MOST the specified combination of srch attrib.
mov ah, [Buf1+bx+03h] ; File attributes
cmp [@@NoSubDirs], 0 ; Allowed to return subdirs?
jz @@SubDirOk
and ah, NOT 10h ; Subdirectory returned as file
@@SubDirOk:
mov al, [es:di+0Ch] ; Search attributes
not al
and al, ah ; Compare attributes
and al, 10011110b ; Ignore bits 0, 5 and 6
jnz @@NextEntry
push cs
pop es
ASSUME es:ResCode
; Check if "." entry.
test [BYTE Buf1+bx+02h], 01h
jz @@NoDot
cmp [@@Root], 0 ; No "." in root.
jnz @@NoDot
; Return . file if it matches filespec.
mov si, OFFSET FNameBuf2
call MatchDot
jc @@NoDot
les di, [@@DTA]
ASSUME es:NOTHING
; Write directory entry for . file
mov eax, [@@DirBlock]
mov [BYTE es:di], 0d2h ; Win95 kludge
mov [es:di+0Dh], eax ; Save current dir block
mov ax, 01h ; Flag "." file returned
or ah, [@@NoSubDirs] ; Keep subdir flag bit
mov [es:di+11h], ax ; Save offset of found entry
add di, 15h ; Point to found file field
; Transfer the filename
mov eax, ' .'
stosd
mov eax, ' '
stosd
stosw
stosb
mov al, [Buf1+14h+03h] ; Attributes
stosb
add di, 10 ; Reserved field
lea si, [Buf1+14h+08h] ; Timestamp field
lodsd
call Unix2DosTime
stosd
xor ax, ax
stosw ; Cluster #
movsd ; File size
mov [BufUsed], 1
clc
jmp @@Done
; Return ".." if attributes match
@@DotDot:
mov bx, 14h
mov si, OFFSET FNameBuf2
call MatchDotDot
jc @@NextEntry
les di, [@@DTA]
ASSUME es:NOTHING
; Match file attributes with AT MOST the specified combination of srch attrib.
mov al, [es:di+0Ch] ; Search attributes
not al
and al, [Buf1+14h+03h] ; File attributes
and al, 10011110b ; Ignore bits 0, 5 and 6
jnz @@NextEntry
; Write directory entry for .. file
mov eax, [@@DirBlock]
mov [BYTE es:di], 0d2h ; Win95 kludge
mov [es:di+0Dh], eax ; Save current dir block
mov ax, 14h ; First entry completed
or ah, [@@NoSubDirs]
mov [es:di+11h], ax ; Save offset of found entry
add di, 15h ; Point to found file field
; Transfer the filename
mov eax, ' ..'
stosd
mov eax, ' '
stosd
stosw
stosb
mov al, [Buf1+14h+03h] ; Attributes
stosb
add di, 10 ; Reserved field
lea si, [Buf1+14h+08h] ; Timestamp field
lodsd
call Unix2DosTime
stosd
xor ax, ax
stosw ; Cluster #
movsd ; File size
mov [BufUsed], 1
clc
jmp @@Done
ASSUME es:ResCode
@@NoDot:
; See if filename is valid in DOS.
test [BYTE Buf1+bx+03h], 40h
jz @@MoveFileName
; Convert long filenames?
cmp [ConvertLong], 0
je @@NextEntry
; Convert long filename to valid name in FNameBuf
lea si, [Buf1+bx+1Fh]
mov di, OFFSET FNameBuf
movzx cx, [BYTE si-1]
call ConvertFilename
jmp @@MatchFilename
; Move filename to FNameBuf, converting it to upper case
@@MoveFilename:
lea si, [Buf1+bx+1Fh]
mov di, OFFSET FNameBuf
mov cl, [Buf1+bx+1Eh]
xor ch, ch
@@MoveChar:
lodsb
call UpCase
stosb
loop @@MoveChar
xor al, al
stosb ; Zero terminate
; Check if filename matches search template
@@MatchFilename:
mov dx, OFFSET FNameBuf ; Filename
push bx
mov bx, OFFSET FNameBuf2 ; Search template, ASCIIZ
call Match
pop bx
jc @@NextEntry
; Match found - set directory entry
les di, [@@DTA]
ASSUME es:NOTHING
mov [BYTE es:di], 0d2h ; Win95 kludge
mov eax, [@@DirBlock]
mov [es:di+0Dh], eax ; Save current dir block
mov ax, [@@LastEntry]
or ah, [@@NoSubDirs]
mov [es:di+11h], ax ; Save offset of found entry
add di, 15h ; Point to found file field
; Pad with spaces.
push di
mov cx, 11
mov al, ' '
rep stosb
pop di
; Transfer the filename
mov si, OFFSET FNameBuf
mov cx, 9
lea dx, [di+8]
@@Tf1:
lodsb
or al, al
je @@TfDone
cmp al, '.'
je @@TfExt
stosb
loop @@Tf1
; Transfer the extension
@@TfExt:
mov cx, 3
mov di, dx
@@TfExt1:
lodsb
call UpCase
or al, al
je @@TfDone
stosb
loop @@TfExt1
@@TfDone:
mov di, dx
add di, 3
mov al, [Buf1+bx+03h] ; Attributes
and al, 10111111b ; Mask out 8.3 filename bit
cmp [@@NoSubDirs], 0 ; Allowed to return subdir?
jz @@AttrOk
and al, NOT 10h ; Return subdir as file
@@AttrOk:
stosb
add di, 10 ; Reserved field
lea si, [Buf1+bx+08h] ; Timestamp field
lodsd
call Unix2DosTime
stosd
xor ax, ax
stosw ; Cluster #
movsd ; File size
mov [BufUsed], 1
clc
jmp @@Done
@@NoMoreFiles:
stc
mov [@@Result], errNoMoreFiles
@@Done:
pop es
popad
jnc @@Exit
mov ax, [@@Result]
@@Exit:
ret
ENDP FindNext
;---------------------------------------------------------------------
; Seek from End of File
; NOTE Seems like this function never gets called! DOS changes the
; file pointers itself.
PROC Seek STDCALL
DEFASSUME
LOCAL @@RetValue:DWORD
pushad
cmp [WORD es:di], 0 ; Open files count
jne @@SFTOk
mov [WORD @@RetValue], errInvalidHandle
stc
jmp @@Done
@@SFTOk:
shl ecx, 16
mov cx, dx ; Offset from end of file in ECX
mov eax, [es:di+11h] ; File size
sub eax, ecx
jnc @@1
xor eax, eax
@@1:
mov [es:di+15h], eax ; New file pos.
mov [@@RetValue], eax
clc
@@Done:
popad
jnc @@2
mov ax, [WORD @@RetValue]
jmp @@Exit
@@2: mov ax, [WORD @@RetValue]
mov dx, [WORD @@RetValue+2]
@@Exit:
ret
ENDP Seek
;---------------------------------------------------------------------
; Extended Open File
; Never gets called under DR-DOS 6.0 which doesn't support extended open.
PROC ExtOpen STDCALL
DEFASSUME
LOCAL @@OpenMode, @@RetValue
pushad
push fs
lfs si, [ExtOpenMode]
mov ax, [fs:si] ; Open mode
mov bx, [WORD LOW AccMode] ; Exchange with normal open mode
xchg [fs:bx], ax
mov [@@OpenMode], ax
call Open
mov [@@RetValue], ax
mov ax, [@@OpenMode]
mov [fs:bx], ax
jc @@Fail
mov [@@RetValue], 01h ; File opened
jmp @@Done
@@Fail:
@@Done:
pop fs
popad
jnc @@Succeeded
mov ax, [@@RetValue]
jmp @@Exit
@@Succeeded:
mov cx, [@@RetValue]
@@Exit:
ret
ENDP ExtOpen
;------------------------------------------------------------------------
; Convert logical sector number to Cyl/Head/Sect in CX, DX, and AL
; as passed to INT 13h. Logical sector passed in ECX.
PROC LogToCHS
add ecx, [LBAStart]
movzx ax, [nSecs]
mul [nHeads]
mov bx, ax ; Sectors/track * heads
mov ax, cx
mov edx, ecx
shr edx, 16 ; DX:AX = logical sector #
div bx
push ax ; Cylinder
mov ax, dx
div [nSecs]
movzx dx, al ; Head
mov cl, ah ; Sector
inc cl
pop ax ; Cylinder
mov dh, dl ; Head
mov ch, al
xor al, al
shr ax, 2
or cl, al ; bits 8 and 9 of cyl. number go here
xor al, al
shr ax, 2
or dh, al ; bits 10 and 11 of cyl (BIOS extension)
ret
ENDP LogToCHS
;------------------------------------------------------------------------
; Read sectors from disk.
PROC DiskRead PASCAL
DEFASSUME
ARG @@Sector:DWORD, @@NumSectors:WORD, @@Dest:DWORD
LOCAL @@CurrSector:DWORD, @@SectorsToRead:WORD, @@CurrDest:DWORD
pushad
push es
mov eax, [@@Sector]
mov [@@CurrSector], eax
add eax, [LBAstart]
mov [DWORD LOW DiskAddrPkt.Sector], eax
mov ax, [@@NumSectors]
mov [@@SectorsToRead], ax
mov [DiskAddrPkt.Count], ax
mov eax, [@@Dest]
mov [@@CurrDest], eax
mov [DiskAddrPkt.Buffer], eax
cmp [AccessMethod], Method_Ext
jne @@ReadCHS
; Read using LBA addressing
mov dl, [PhysDrv]
mov si, OFFSET DiskAddrPkt
mov ah, 42h
int 13h
jc @@Done
jmp @@CopyToCache
; Read using CHS addressing
@@ReadCHS:
mov ecx, [@@CurrSector]
call LogToCHS
mov al, [BYTE @@NumSectors]
cmp [Multitrack], 0
jnz @@DoInt13
; Multitrack operations not supported
push cx
and cl, 3Fh
mov al, [nSecs]
sub al, cl
inc al ; Sectors left on this track
xor ah, ah
cmp ax, [@@SectorsToRead]
jbe @@3
mov al, [BYTE @@SectorsToRead] ; Read SectorsToRead sectors
@@3: pop cx
@@DoInt13:
mov dl, [PhysDrv]
mov ah, 02h
les bx, [@@CurrDest]
push ax
int 13h
pop ax
jc @@Done
and eax, 0FFh
les di, [@@CurrDest] ; Buffer
movzx cx, al ; Number of sectors
mov edx, [@@CurrSector] ; First sector
add [@@CurrSector], eax
sub [@@SectorsToRead], ax
push dx
mov dx, 200h
mul dx
pop dx
add [WORD @@CurrDest], ax
; Check if any more sectors to read
@@SectorsLeft:
cmp [@@SectorsToRead], 0
jnz @@ReadCHS
; Copy read sectors to cache.
@@CopyToCache:
cmp [CacheOn], 0
jz @@Done
cmp [XMSError], 0
jnz @@Done
mov edx, [@@Sector]
les di, [@@Dest]
mov cx, [@@NumSectors]
@@CacheLoop:
call GetFreeCacheEntry ; Get free cache entry in BX.
or bx, bx
sete [XMSError]
jz @@Done
push cx
mov ecx, edx
call CacheInsert ; Insert sector ECX at ES:DI at entry BX.
pop cx
add edx, 1
add di, 200h
loop @@CacheLoop
clc
@@Done:
pop es
popad
ret
ENDP DiskRead
;------------------------------------------------------------------------
; Read logical sectors into memory.
PROC ReadSector PASCAL
DEFASSUME
ARG @@Sector:DWORD, @@NumSectors:WORD, @@Dest:DWORD
LOCAL @@SectorsToRead
pushad
push es
@@Start:
mov dx, [@@NumSectors]
mov [@@SectorsToRead], dx
cmp [CacheOn], 0
jz @@DiskRead
cmp [XMSError], 0
jnz @@DiskRead
@@SearchCache:
mov ecx, [@@Sector]
call SearchCache ; Search for sector ECX in cache
or bx, bx
jz @@CacheMiss
; Cache hit. Move the sector to the destination.
@@CacheHit:
les di, [@@Dest]
call ReadCacheSector ; Read sector in entry BX
or ax, ax
sete [XMSError]
jz @@DiskRead
inc [@@Sector]
add [WORD @@Dest], 200h
sub [@@NumSectors], 1 ; Sectors left to read?
jnz @@SearchCache
jmp @@Done
@@CacheMiss:
; Determine number of consecutive sectors to read from disk.
mov ecx, [@@Sector]
mov [@@SectorsToRead], 0
@@ConsecutiveSectors:
inc ecx
inc [@@SectorsToRead]
mov ax, [@@NumSectors]
cmp ax, [@@SectorsToRead]
jbe @@DiskRead
push ecx
call SearchCache
pop ecx
or bx, bx
jz @@ConsecutiveSectors
; Read sectors from disk.
@@DiskRead:
movzx eax, [@@SectorsToRead]
call NEAR DiskRead, [@@Sector], ax, [@@Dest]
jc @@Done
add [@@Sector], eax
sub [@@NumSectors], ax
jz @@Done ; No sectors left to read
mov dx, 200h
mul dx
add [WORD @@Dest], ax
jmp @@Start
@@Done:
pop es
popad
ret
ENDP ReadSector
;---------------------------------------------------------------------
; Inserts a sector into the cache at a free entry. Sector number ECX,
; entry number BX, memory address ES:DI. Returns CF=1 on XMS error.
PROC CacheInsert STDCALL
LOCAL @@EntryNum, @@SectorAddr:DWORD, @@Sector:DWORD
DEFASSUME
pushad
mov si, OFFSET XMoveStruc
mov [WORD @@SectorAddr], di
mov [WORD @@SectorAddr+2], es
mov [@@Sector], ecx
mov [@@EntryNum], bx
; Store the sector number
mov eax, [@@Sector]
mov [DWORD EntryBuf], eax
mov [WORD EntryBuf+0Ch], gs ; Data segment
; Insert the new sector at the head of the priority list.
; next[x] <- next[sentinel]
mov [XMoveStruc.Length], 2
mov ax, [hCacheLists]
mov [XMoveStruc.SourceHandle], ax
mov [XMoveStruc.DestHandle], 0
mov [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+0Ah
mov [WORD (XMoveStruc.DestOffset)+2], ds
mov [XMoveStruc.SourceOffset], 0Ah ; Next field of sentinel
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; prev[next[sentinel]] <- x
mov ax, [WORD EntryBuf+0Ah]
mov dx, CacheEntrySize
mul dx
add ax, 08h
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.SourceHandle], 0
mov bx, [@@EntryNum]
mov [WORD EntryBuf+CacheEntrySize], bx
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; next[sentinel] <- x
mov [XMoveStruc.DestOffset], 0Ah
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; prev[x] <- sentinel
mov [EntryBuf+08h], 0
; Compute hash function
mov eax, [@@Sector]
mov edx, eax
shr edx, 16 ; Sector in DX:AX
div [HashSize] ; Hash slot in DX
shl edx, 1 ; Offset into hash table
mov edi, edx ; Save offset
; Insert the entry at the head of the hash table
; next[x] <- head
mov ax, [hHashTable]
mov [XMoveStruc.SourceHandle], ax
mov [XMoveStruc.SourceOffset], edx
mov [XMoveStruc.DestHandle], 0
mov [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+06h
mov [WORD (XMoveStruc.DestOffset)+2], ds
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; if head <> NIL then prev[head] <- x
mov ax, [WORD EntryBuf+06h]
or ax, ax
jz @@HashInsert2
mov dx, CacheEntrySize
mul dx
add ax, 04h ; Prev
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
@@HashInsert2:
; head <- x
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov ax, [hHashTable]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.DestOffset], edi ; Saved offset into hash table
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; prev[x] <- NIL
mov [WORD EntryBuf+04h], 0
; Write the entry
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov ax, [@@EntryNum]
mov dx, CacheEntrySize
mul dx
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf
mov [XMoveStruc.Length], CacheEntrySize
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; Copy the sector to the cache
mov [XMoveStruc.Length], 512
mov eax, [@@SectorAddr]
mov [XMoveStruc.SourceOffset], eax
mov [XMoveStruc.SourceHandle], 0
mov ax, [hCacheSectors]
mov [XMoveStruc.DestHandle], ax
movzx eax, [@@EntryNum]
shl eax, 9
mov [XMoveStruc.DestOffset], eax
mov si, OFFSET XMoveStruc
mov ah, 0Bh
call [XMSEntry]
or ax, ax
clc
jnz @@Done
@@XMSError:
mov [XMSError], 1 ; Flag XMS error
stc
@@Done: popad
ret
ENDP CacheInsert
;---------------------------------------------------------------------
; Searches for a sector in the cache. Sector number in ECX. Returns
; cache entry in BX. If sector is not in cache, BX=0000.
PROC SearchCache STDCALL
DEFASSUME
LOCAL @@Sector:DWORD
; Compute the hash function (sector number modulo slots in hash table)
cmp [XMSError], 0
jnz @@XMSError
mov [@@Sector], ecx
mov ax, cx
mov edx, ecx
shr edx, 16 ; Sector in DX:AX
div [HashSize] ; Hash slot in DX
; Extract the linked list pointer from the hash table
mov [XMoveStruc.Length], 2
mov ax, [hHashTable]
mov [XMoveStruc.SourceHandle], ax
shl edx, 1
mov [XMoveStruc.SourceOffset], edx
mov [XMoveStruc.DestHandle], 0
mov [WORD XMoveStruc.DestOffset], OFFSET EntryBuf
mov [WORD (XMoveStruc.DestOffset)+2], ds
mov si, OFFSET XMoveStruc
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
mov bx, [WORD EntryBuf]
; Walk the linked list and search for the sector
; Entry format: Offset Size Descr
; 00h DWORD Sector number
; 04h WORD Prev entry in hash chain
; 06h WORD Next entry in hash chain
; 08h WORD Prev entry in priority list
; 0Ah WORD Next entry in priority list
; 0Ch WORD ResData segment of owner drive
mov [XMoveStruc.Length], CacheEntrySize
mov ax, [hCacheLists]
mov [XMoveStruc.SourceHandle], ax
@@SearchList:
or bx, bx
je @@Done ; Sector not in cache, exit.
; Copy the entry into first bytes of buffer
mov ax, bx
mov dx, CacheEntrySize
mul dx ; Offset of entry in DX:AX
mov [WORD XMoveStruc.SourceOffset], ax
mov [(WORD XMoveStruc.SourceOffset)+2], dx
mov si, OFFSET XMoveStruc
mov ah, 0Bh
push bx
call [XMSEntry]
pop bx
or ax, ax
jz @@XMSError
; Examine the entry in buffer
mov eax, [@@Sector]
cmp [DWORD EntryBuf], eax
jne @@Search1
mov ax, gs
cmp [WORD EntryBuf+0Ch], ax ; Check if it's the right drive.
je @@Done ; Sector found, exit with entry in BX.
@@Search1:
mov bx, [WORD EntryBuf+06h] ; Next entry in chain
jmp @@SearchList
@@XMSError:
mov [XMSError], 1 ; Flag XMS error
xor bx, bx
@@Done: ret
ENDP SearchCache
;---------------------------------------------------------------------
; Delete a cache entry. Entry number in BX. Entry must be in use.
; Returns carry set on XMS error.
PROC DeleteCacheEntry
pushad
; Read the entry into buffer
mov [XMoveStruc.Length], CacheEntrySize
mov ax, bx
mov dx, CacheEntrySize
mul dx
mov [WORD XMoveStruc.SourceOffset], ax
mov [WORD (XMoveStruc.SourceOffset)+2], dx
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; Delete the entry from the hash chain
; if prev[x] <> NIL
; then next[prev[x]] <- next[x]
; else head <- next[x]
; if next[x] <> NIL
; then prev[next[x]] <- prev[x]
mov ax, [WORD EntryBuf+04h] ; Prev field.
or ax, ax ; See if entry is head
je @@NewHead
mov dx, CacheEntrySize
mul dx
add ax, 06h ; Next field
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+06h
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov [XMoveStruc.Length], 2
mov si, OFFSET XMoveStruc
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
jmp @@UpdateNextChainEntry
@@NewHead:
xor edx, edx
mov ax, [WORD EntryBuf]
mov dx, [WORD EntryBuf+2] ; Sector in DX:AX
div [HashSize] ; Hash slot in DX
mov ax, [hHashTable]
mov [XMoveStruc.DestHandle], ax
shl edx, 1
mov [XMoveStruc.DestOffset], edx
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+06h
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov [XMoveStruc.Length], 2
mov si, OFFSET XMoveStruc
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
@@UpdateNextChainEntry:
mov ax, [WORD EntryBuf+06h]
or ax, ax
je @@DeleteEntry2
mov dx, CacheEntrySize
mul dx
add ax, 04h
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+04h
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov [XMoveStruc.Length], 2
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
@@DeleteEntry2:
; Delete the entry from the priority list.
; next[prev[x]] <- next[x]
; prev[next[x]] <- prev[x]
mov ax, [WORD EntryBuf+08h] ; Prev
mov dx, CacheEntrySize
mul dx
add ax, 0Ah ; Next field
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+0Ah
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov [XMoveStruc.Length], 2
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
mov ax, [WORD EntryBuf+0Ah] ; Next
mov dx, CacheEntrySize
mul dx
add ax, 08h ; Prev field
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+08h
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
clc
jmp @@Done
@@XMSError:
stc
@@Done: popad
ret
ENDP DeleteCacheEntry
;---------------------------------------------------------------------
; Return a free cache entry. If cache is full, then oldest entry is
; deleted. Free entry returned in BX. BX=0000 if an XMS error occurs.
PROC GetFreeCacheEntry STDCALL
LOCAL Entry
pushad
mov bx, [FreeEntry]
or bx, bx
jz @@GetOldestEntry
mov [Entry], bx
dec [FreeEntry]
jmp @@Done
; Get the oldest entry.
@@GetOldestEntry:
mov [XMoveStruc.Length], 2
mov ax, [hCacheLists]
mov [XMoveStruc.SourceHandle], ax
mov [XMoveStruc.SourceOffset], 08h ; Tail of priority list
mov [XMoveStruc.DestHandle], 0
mov [WORD XMoveStruc.DestOffset], OFFSET EntryBuf
mov [WORD (XMoveStruc.DestOffset)+2], ds
mov si, OFFSET XMoveStruc
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
mov bx, [WORD EntryBuf] ; Entry to be deleted
mov [Entry], bx
call DeleteCacheEntry
jc @@XMSError
jmp @@Done
@@XMSError:
mov [XMSError], 1 ; Flag XMS error
mov [Entry], 0
@@Done: popad
mov bx, [Entry]
ret
ENDP GetFreeCacheEntry
;---------------------------------------------------------------------
; Read a sector from the cache. Cache entry in BX. The entry is moved
; to the head of the priority list. Sector is copied to ES:DI.
; Returns AX=0 if XMS error.
PROC ReadCacheSector STDCALL
LOCAL @@Entry
mov [@@Entry], bx
mov [XMoveStruc.Length], 512
mov ax, [hCacheSectors]
mov [XMoveStruc.SourceHandle], ax
movzx eax, bx
shl eax, 9 ; Multiply by 512
mov [XMoveStruc.SourceOffset], eax
mov [XMoveStruc.DestHandle], 0
mov [WORD XMoveStruc.DestOffset], di
mov [(WORD XMoveStruc.DestOffset)+2], es
mov si, OFFSET XMoveStruc
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; Remove the entry from the priority list.
; Entry 0 in priority list is a sentinel.
; next[prev[x]] <- next[x]
mov [XMoveStruc.Length], 2
mov [XMoveStruc.SourceHandle], 0
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+0Ah ; Next
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov ax, [WORD EntryBuf+08h] ; Prev
mov dx, CacheEntrySize
mul dx ; Offset
add ax, 0Ah ; Next field of previous entry
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; prev[next[x]] <- prev[x]
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+08h ; Prev
mov ax, [WORD EntryBuf+0Ah] ; Next
mov dx, CacheEntrySize
mul dx ; Offset
add ax, 08h ; Prev field
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; Insert the entry at the head of the priority list
; next[x] <- next[sentinel]
mov ax, [hCacheLists]
mov [XMoveStruc.SourceHandle], ax
mov [XMoveStruc.SourceOffset], 0Ah ; Next field of sentinel
mov ax, [@@Entry] ; Entry number
mov dx, CacheEntrySize
mul dx
mov di, dx
shl edi, 16
mov di, ax
add edi, 0Ah ; Next field
mov [XMoveStruc.DestOffset], edi
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; prev[next[sentinel]] <- x
mov [XMoveStruc.DestHandle], 0
mov [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+CacheEntrySize
mov [WORD (XMoveStruc.DestOffset)+2], ds
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
mov ax, [WORD EntryBuf+CacheEntrySize] ; next[sentinel]
mov dx, CacheEntrySize
mul dx
add ax, 08h ; prev field
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov ax, [@@Entry]
mov [WORD EntryBuf+CacheEntrySize], ax ; Current entry
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; next[sentinel] <- x
mov [XMoveStruc.DestOffset], 0Ah
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
; prev[x] <- sentinel
mov [WORD EntryBuf+CacheEntrySize], 0
mov ax, [@@Entry]
mov dx, CacheEntrySize
mul dx
add ax, 08h ; prev field
adc dx, 0
mov [WORD XMoveStruc.DestOffset], ax
mov [WORD (XMoveStruc.DestOffset)+2], dx
mov ah, 0Bh
call [XMSEntry]
or ax, ax
jz @@XMSError
jmp @@Done
@@XMSError:
mov [XMSError], 1 ; Flag XMS error
@@Done: ret
ENDP ReadCacheSector
;---------------------------------------------------------------------
; Read specified file sectors. Arguments:
; @@Sector - starting file sector
; @@NumSectors - number of sectors to read
; @@FNode - FNode sector number of file
; @@Dest - destination buffer
; Uses Buf4. Return CF on error.
PROC ReadFileSector PASCAL
DEFASSUME
ARG @@Sector:DWORD, @@NumSectors, @@FNode:DWORD, @@Dest:DWORD
LOCAL @@Entries:WORD
pushad
; Read the FNode
call NEAR ReadSector, [@@FNode], 1, ds (OFFSET Buf4)
mov bx, OFFSET Buf4+38h
@@Start:
mov eax, [@@Sector]
movzx cx, [BYTE bx+05h] ; number of entries in sector
mov [@@Entries], cx
test [BYTE bx], 80h ; Internal or external node?
jnz @@Internal
; Search external node - extents.
sub bx, 4 ; Point to 12 bytes before first entry
xor dx, dx ; Entry counter
@@NextExtent:
add bx, 12 ; Point to next entry
inc dx
cmp dx, [@@Entries] ; Any entries left?
ja @@Error
mov ecx, [DWORD bx] ; First file sector in this extent
cmp ecx, eax
ja @@Error ; Too high - something's wrong
add ecx, [DWORD bx+04h] ; Add number of sectors in extent
sub ecx, eax ; Sector in this extent?
jna @@NextExtent
; Extent found - calculate disk sector and read.
mov esi, ecx ; Number of sectors left in extent
mov ecx, eax
sub ecx, [DWORD bx] ; Offset into extent
add ecx, [DWORD bx+08h] ; First logical sector number
; Calculate number of sectors to read.
movzx edi, [@@NumSectors]
cmp esi, edi
jbe @@1
mov si, di
@@1:
call NEAR ReadSector, ecx, si, [@@Dest]
jc @@Error
sub [@@NumSectors], si
clc
jz @@Done
and esi, 0FFFFh
add [@@Sector], esi
; Reread FNode
call NEAR ReadSector, [@@FNode], 1, ds (OFFSET Buf4)
mov bx, OFFSET Buf4+38h
mov ax, 200h
mul si
add [WORD LOW @@Dest], ax ; High word in DX not used, should be 0.
jmp @@Start
; Search internal node
@@Internal:
xor dx, dx ; Entry counter
@@NextSubtree:
add bx, 8 ; Point to next entry
inc dx
cmp dx, [@@Entries] ; Any entries left?
ja @@Error
cmp eax, [DWORD bx] ; If below, then sector is in this tree.
jae @@NextSubtree
; Subtree found - read sector and recurse.
call NEAR ReadSector, [DWORD bx+04h], 1, ds (OFFSET Buf4)
jc @@Error
mov bx, OFFSET Buf4+0Ch ; Point to allocation structure
jmp @@Start
@@Error:
stc
@@Done: popad
ret
ENDP ReadFileSector
;---------------------------------------------------------------------
; Convert a character in AL to upper case.
PROC UpCase
DEFASSUME
cmp al, 128
jae @@Extended
cmp al, 'a'
jb @@Done
cmp al, 'z'
ja @@Done
sub al, 'a'-'A'
@@Done: ret
@@Extended:
push bx ds
push cs
pop ds
mov bx, OFFSET UpCaseTbl-128
xlatb
pop ds bx
ret
ENDP UpCase
;---------------------------------------------------------------------
; Checks if the character in AL is a valid filename character.
; CF=1 if invalid. Registers preserved.
PROC FileChar
DEFASSUME
push ax ds
push cs
pop ds
cmp al, [MinPerm]
jb @@Fail
cmp al, [MaxPerm]
ja @@Fail
cmp al, [MinExcl]
jb @@1
cmp al, [MaxExcl]
jna @@Fail
@@1:
push cx di es
push ds
pop es
ASSUME es:ResCode
mov cl, [NumTerm]
xor ch, ch
mov di, OFFSET TermChars
cld
repne scasb
pop es di cx
je @@Fail
clc
jmp @@Done
@@Fail:
stc
@@Done:
pop ds ax
ret
ENDP FileChar
;---------------------------------------------------------------------
; Checks if a filename matches a spec.
; Filename at DS:DX. Filespec at ES:BX.
; Returns CF=0 if match.
PROC Match
DEFASSUME
ASSUME ds:NOTHING
push bx cx dx si di
cld
mov si, dx
@@Next:
mov ah, [es:bx]
inc bx
lodsb
; Check if the character is valid in filenames
or al, al
je @@Valid
cmp al, '.'
je @@Valid
call FileChar
jc @@Fail
@@Valid:
or ah, ah ; End of filespec?
jne @@1
or al, al
jne @@Fail
jmp @@Succeed
@@1: cmp ah, '?'
jne @@3
cmp al, '.'
je @@2
or al, al
je @@2
jmp @@Next
@@2: dec si
jmp @@Next
@@3: cmp ah, '.'
jne @@4
or al, al
jne @@4
dec si
jmp @@Next
@@4: cmp ah, al
je @@Next
@@Fail:
stc
jmp @@Done
@@Succeed:
clc
@@Done: pop di si dx cx bx
ret
ENDP Match
;---------------------------------------------------------------------
; Check if filespec at DS:SI matches . file. Returns CF=0 if match.
; Filespec matched: At least one <?>, optional <dot>, optional more <?>.
PROC MatchDot
NOASSUME
ASSUME cs:ResCode
push ax si
; Match one <?>
lodsb
cmp al, '?'
jne @@Fail
; Eat all consecutive <?>
@@1: lodsb
cmp al, '?'
je @@1
or al, al ; End of filespec?
je @@Ok
cmp al, '.' ; Match dot (separator)
jne @@Fail
; Eat all consecutive <?>
@@2: lodsb
cmp al, '?'
je @@2
or al, al ; End of filespec?
jne @@Fail
@@Ok: clc
jmp @@Done
@@Fail: stc
@@Done: pop si ax
ret
ENDP MatchDot
;---------------------------------------------------------------------
; Check if filespec at DS:SI matches .. file. Returns CF=0 if match.
; Filespec matched: At least two <?>, optional <dot>, optional more <?>.
PROC MatchDotDot
NOASSUME
ASSUME cs:ResCode
push ax si
; Match two <?>
lodsw
cmp ax, '??'
jne @@Fail
; Eat all consecutive <?>
@@1: lodsb
cmp al, '?'
je @@1
or al, al ; End of filespec?
je @@Ok
cmp al, '.' ; Match dot (separator)
jne @@Fail
; Eat all consecutive <?>
@@2: lodsb
cmp al, '?'
je @@2
or al, al ; End of filespec?
jne @@Fail
@@Ok: clc
jmp @@Done
@@Fail: stc
@@Done: pop si ax
ret
ENDP MatchDotDot
;---------------------------------------------------------------------
; Convert an HPFS filename to a valid DOS filename. Only to be called
; from FindNext.
; DS:SI - filename to convert. ES:DI - buffer to place converted name.
; CX - length of filename.
PROC ConvertFilename STDCALL
DEFASSUME
LOCAL @@Len:WORD
pusha
cld
mov [@@Len], cx
xor ah, ah
@@ConvName:
jcxz @@ConvExt
lodsb
dec cx
cmp al, '.'
je @@ConvExt
call FileChar ; Character valid in filenames?
jc @@ConvName
call UpCase
stosb
inc ah
cmp ah, 8
jb @@ConvName
@@ConvExt:
or ah, ah ; Empty name?
jnz @@Conv1
mov eax, 'SFPH' ; Dummy name
stosd
@@Conv1:
mov al, '.'
stosb
lea si, [Buf1+bx+1Fh] ; Point to filename
mov cx, [@@Len] ; Length of filename
call CRC16 ; CRC16 in DX
mov ax, dx
xor dx, dx
; Convert to base-41
mov bx, OFFSET ConvTable ; Translation table
mov cx, 3
@@ConvBase:
mov si, 41
div si ; Remainder in DX (DL)
xchg al, dl
xlatb
stosb
mov al, dl
xor dx, dx
loop @@ConvBase
xor al, al ; Zero terminate
stosb
popa
ret
ENDP ConvertFilename
;---------------------------------------------------------------------
; Find a directory entry. FNode sector of directory passed in ECX.
; Filename at DS:DX. Assumes DS=CS.
; CF=1 if failed.
; The sector is read into Buf1, and BX contains the offset to the
; specified entry on return. Uses last half of FNameBuf
PROC FindDirEntry STDCALL
DEFASSUME
ASSUME es:ResCode
LOCAL @@Len:BYTE, @@BytesRead:WORD, @@Sector:DWORD
push es
push cs
pop es
mov [@@Sector], ecx
; Count characters in filename.
mov di, dx
xor al, al
mov cx, 0FFh
cld
repne scasb
neg cl
sub cl, 2
or cl, cl
jz @@Fail
mov [@@Len], cl
mov [@@BytesRead], 200h ; Bytes of directory block in memory
; Read the FNode of the directory
call NEAR ReadSector, [@@Sector], 1, es (OFFSET Buf1)
jc @@Fail
mov ecx, [DWORD Buf1+48h]
mov [@@Sector], ecx
call NEAR ReadSector, ecx, 4, es (OFFSET Buf1)
jc @@Fail
mov bx, 14h ; Offset of first dir. entry
cld
cmp [ConvertLong], 0
je @@TestEntry
; Traverse the directory B Tree to find a match. Convert long filenames.
; All entries must be searched.
@@ChkValidName:
mov di, OFFSET FNameBuf+64 ; Load regs for call or move later
lea si, [Buf1+bx+1Fh]
movzx cx, [BYTE si-1]
test [Buf1+bx+03h], 40h
jz @@DOSName
; Convert filename
call ConvertFilename
jmp @@CompareName
; Copy filename to buffer
@@DosName:
test [Buf1+bx+02h], 01h ; Skip '.' file
jnz @@NextLong
push cx di
@@CopyName:
lodsb
call UpCase
stosb
loop @@CopyName
xor al, al
stosb
pop di cx
@@CompareName:
mov si, dx
@@Cmp1:
lodsb
or al, al
jz @@EndOfTmpl ; End of search filename
scasb
je @@Cmp1
jmp @@NextLong
@@EndOfTmpl:
cmp [BYTE es:di], 0 ; Check that filename in entry also ends
je @@Succeed
@@NextLong:
; See if there's a B Tree pointer
test [BYTE Buf1+bx+02h], 04h
jz @@NoBTree
; Go down the tree
add bx, [WORD Buf1+bx]
mov eax, [DWORD Buf1+bx-04h] ; B Tree pointer
mov [@@Sector], eax
call NEAR ReadSector, eax, 4, es (OFFSET Buf1)
jc @@Fail
mov bx, 14h
jmp @@ChkValidName ; Search this block
@@NoBTree:
; Move to next entry
@@NextEntry:
test [BYTE Buf1+bx+02h], 08h ; See if last entry
jnz @@Up
add bx, [WORD Buf1+bx] ; Point to next entry
jmp @@ChkValidName ; Continue search
; Go up the tree
@@Up:
mov eax, [DWORD Buf1+0Ch] ; Parent node
call NEAR ReadSector, eax, 4, es (OFFSET Buf1)
jc @@Fail
cmp [BYTE Buf1+03h], 0F7h ; FNode signature
je @@Fail
; Go through this directory block to find the entry that we came from.
mov bx, 14h
@@SrchParent:
test [Buf1+bx+02h], 04h ; Entry has B tree pointer?
jz @@NotParent
mov si, bx
add si, [WORD Buf1+bx]
mov ecx, [@@Sector]
cmp [DWORD Buf1+si-4], ecx ; Is parent?
jne @@NotParent
mov [@@Sector], eax
jmp @@NextEntry
@@NotParent:
test [Buf1+bx+02h], 08h ; Last entry in block?
jnz @@Fail
add bx, [WORD Buf1+bx]
jmp @@SrchParent
; Traverse the directory B Tree to find a match. No long filenames.
@@TestEntry:
cld
; Compare the filename length byte
mov cl, [@@Len]
cmp cl, [Buf1+bx+1Eh]
jna @@1
mov cl, [Buf1+bx+1Eh] ; Entry shorter than filename
; Compare the filenames
@@1:
xor ch, ch
mov di, dx
lea si, [Buf1+bx+1Fh]
@@Compare:
lodsb ; Load one character
call UpCase ; Convert to upper case
scasb ; Compare
loope @@Compare
jb @@Next
ja @@ChkBTree
mov cl, [@@Len] ; See which of the names is the longest
cmp cl, [Buf1+bx+1Eh]
je @@Succeed
ja @@Next
; See if there's a B Tree pointer
@@ChkBTree:
test [BYTE Buf1+bx+02h], 04h
jz @@Fail
add bx, [WORD Buf1+bx]
mov ecx, [DWORD Buf1+bx-4] ; Extract the B Tree pointer
mov [@@Sector], ecx
mov [@@BytesRead], 200h
call NEAR ReadSector, [@@Sector], 1, es (OFFSET Buf1)
mov bx, 14h
jmp @@TestEntry
@@Next:
test [BYTE Buf1+bx+02h], 08h ; See if last entry
jnz @@Fail
add bx, [WORD Buf1+bx] ; Point to next entry
mov ax, [WORD Buf1+bx]
add ax, bx
cmp ax, [@@BytesRead] ; See if enough sectors read.
jb @@TestEntry
cmp [@@BytesRead], 800h
jnb @@Fail ; Whole dir. block already read.
inc [@@Sector]
push bx
mov bx, [@@BytesRead]
add bx, OFFSET Buf1
call NEAR ReadSector, [@@Sector], 1, es bx
pop bx
add [@@BytesRead], 200h
jmp @@TestEntry
@@Fail:
stc
jmp @@Done
@@Succeed:
clc
@@Done:
pop es
ret
ENDP FindDirEntry
;---------------------------------------------------------------------
; Find a file. Fully qualified filename at ES:DI. Offset into
; Buf1 to directory entry returned in BX.
PROC FindFile STDCALL
DEFASSUME
LOCAL @@DirFlag:BYTE, @@CurFNode:DWORD, @@RetValue, @@LongPath:BYTE
LOCAL @@BufStart
pushad
push ds
push cs
pop ds
mov [@@DirFlag], 0 ; Set if current level is also the last.
mov [@@LongPath], 0 ; Set if path is too long for any more subdirs.
mov [@@BufStart], di
mov ecx, [RootFNode]
mov [@@CurFNode], ecx
; Scan past the first backslash.
mov al, '\'
mov cx, 80h
cld
repne scasb
dec di
@@NextLevel:
cmp [@@LongPath], 0
jne @@Fail ; Path too long - can't access directory.
; See if path is too long
mov ax, di
sub ax, [@@BufStart]
cmp ax, MaxPathLength-9
seta [@@LongPath]
mov bx, OFFSET FNameBuf
mov dx, bx
; Move the name of the next subdirectory to FNameBuf
@@MoveName:
inc di
mov al, [es:di]
or al, al
je @@SetFlag
cmp al, '\'
je @@FindEntry
mov [bx], al
inc bx
jmp @@MoveName
@@SetFlag:
mov [@@DirFlag], 1 ; This is the last level
@@FindEntry:
mov [BYTE bx], 0
; Find the directory entry
mov ecx, [@@CurFNode]
push di
call FindDirEntry
mov [@@RetValue], bx
pop di
jc @@Fail
mov ecx, [DWORD Buf1+bx+04h] ; Get FNode pointer
mov [@@CurFNode], ecx
cmp [@@DirFlag], 0
je @@NextLevel
clc
jmp @@Done
@@Fail: stc
@@Done:
pop ds
popad
jc @@Exit
mov bx, [@@RetValue]
@@Exit:
ret
ENDP FindFile
;---------------------------------------------------------------------
; Check if a fully qualified pathname is too long to be the name of a
; directory, and should be handled as a zero-length file. CF=1 if path
; is too long.
PROC ChkPathLength PASCAL
DEFASSUME
ARG @@FileName:DWORD
pushad
push es
les di, [@@FileName]
xor al, al
mov cx, 128
cld
repne scasb
mov al, '\'
std
mov cx, 128
repne scasb
cld
sub di, [WORD LOW @@FileName]
cmp di, MaxPathLength-9
cmc ; CF indicates result
pop es
popad
ret
ENDP ChkPathLength
;---------------------------------------------------------------------
; Convert UNIX (and HPFS) timestamp to DOS timestamp (local time).
; UNIX timestamp passed in EAX and DOS timestamp returned in EAX -
; time in low word, date in high word.
PROC Unix2DosTime STDCALL
DEFASSUME
LOCAL @@Result:DWORD,@@Year,@@Month,@@Day,@@Hour,@@Min,@@Secs
pushad
push ds
push cs
pop ds
sub eax, 24*60*60*3652+TimeZone+60*60
xor edx, edx
mov ebx, 60
div ebx
mov [@@Secs], dx
xor edx, edx
div ebx
mov [@@Min], dx ; Time in minutes in EAX
mov ebx, 1461*24
xor edx, edx
div ebx
shl eax, 2
add eax, 1980
mov [@@Year], ax
mov eax, edx
cmp eax, 366*24
jb @@1
sub eax, 366*24
inc [@@Year]
mov ebx, 365*24
xor edx, edx
div ebx
add [@@Year], ax
mov ax, dx
@@1: ; Now in AX
xor dx, dx
mov bx, 24
div bx
mov [@@Hour], dx
inc ax
test [@@Year], 3
jnz @@2
cmp ax, 60
jna @@3
dec ax
jmp @@2
@@3: jne @@2
mov [@@Month], 2
mov [@@Day], 29
jmp @@DecodeDone
@@2:
xor bx, bx
@@2_1:
mov dl, [DaysInMonth+bx]
xor dh, dh
cmp dx, ax
jnb @@4
sub ax, dx
inc bx
jmp @@2_1
@@4: inc bx
mov [@@Month], bx
mov [@@Day], ax
@@DecodeDone:
; Pack the time in DOS format.
xor eax, eax
mov ax, [@@Day]
mov bx, [@@Month]
shl bx, 5
or ax, bx
mov bx, [@@Year]
sub bx, 1980
shl bx, 9
or ax, bx
shl eax, 16
mov ax, [@@Secs]
shr ax, 1
mov bx, [@@Min]
shl bx, 5
or ax, bx
mov bx, [@@Hour]
shl bx, 11
or ax, bx
mov [@@Result], eax
pop ds
popad
mov eax, [@@Result]
ret
ENDP Unix2DosTime
;---------------------------------------------------------------------
; Compute CRC16 of a string. DS:SI points at the buffer, CX is the
; length of the buffer. CRC16 returned in DX.
PROC CRC16
NOASSUME
ASSUME cs:ResCode
push ax bx
pushf
cld
xor dx, dx
@@1: lodsb
xor ah, ah
xchg ah, al
xor dx, ax
push cx
mov cx, 8
@@2: mov bx, dx
shl dx, 1
and bx, 8000h
jz @@3
xor dx, 1021h
@@3: loop @@2
pop cx
loop @@1
popf
pop bx ax
ret
ENDP CRC16
PROC ThunkReadSector PASCAL FAR
ARG @@Sector:DWORD,@@NumSectors:WORD,@@Dest:DWORD
call NEAR ReadSector, [@@Sector], [@@NumSectors], [@@Dest]
ret
ENDP ThunkReadSector
DB 64 DUP ('RESSTACK')
LABEL ResStack
ENDS ResCode
;================================================== Resident data
SEGMENT ResData
PartitionNr DB 0 ; HPFS partition number
; Partition info
nSecs DB 0 ; Sectors per track
nHeads DW 0 ; Heads
LBAstart DD 0 ; LBA starting sector
AccessMethod DB 0 ; Disk access method
PhysDrv DB 80h
; File system info
RootFNode DD 0 ; FNode sector for root dir.
CDFNode DD 0 ; FNode sector for current dir.
TotalSectors DD 0 ; Partition size in sectors
FreeSectors DD 0 ; # of sectors marked as free in bitmaps
MediaID DB 0 ; Media ID byte from boot block
Volabel DB 12 DUP (0) ; Volume label + an extra null.
LABEL EndResdata
ENDS ResData
;======================================================= Transient section
NOASSUME
SEGMENT CSeg
ASSUME cs:CSeg,ds:DSeg,es:NOTHING,ss:SSeg,fs:ResCode,gs:ResCode
PROC Main
mov ax, DSeg
mov ds, ax
mov es, ax
mov ax, ResCode
mov fs, ax
mov gs, ax
; Write hello message
mov ah, 09h
mov dx, OFFSET MsgHello
int 21h
; Check for DR-DOS or Novell DOS
mov ax, 4452h
stc
int 21h
jc @@NotDrDos
; DR-DOS or Novell DOS found. Check version.
cmp ah, 10h
jne errBadDosVer
cmp al, 72h ; Novell DOS
je @@NovellDOS
mov [DrDos], al ; DR-DOS version code
cmp al, 65h ; DR-DOS 5
je @@Dos3
cmp al, 67h ; DR-DOS 6
je @@Dos3
jmp errBadDosVer
@@NovellDOS:
mov [Novell], 1
jmp @@DosVerOk
@@NotDrDos:
; Check DOS version
mov ax, 3000h
int 21h
cmp al, 3
je @@Major3
jb errBadDOSVer
cmp al, 10
jae errBadDOSVer
jmp @@DosVerOk
; DOS 3.x, check minor version is at least 10
@@Major3:
cmp ah, 10
jb errBadDOSVer
; DOS 3.10-3.x or DR DOS 5 and 6
@@Dos3:
mov [CDSSize], 51h
mov [WORD LOW pCurrCDS], 26Ch
mov [WORD LOW FN1], 92h
mov [WORD LOW AccMode], 23Bh
mov [WORD LOW SrchAttr], 23Ah
@@DosVerOk:
; Get PSP
mov ah, 51h
int 21h
mov [PSP], bx
; Release heap space above end of stack
mov bx, sp
add bx, 15
shr bx, 4
mov ax, ss
add bx, ax
mov ax, [PSP]
sub bx, ax
push es
mov es, ax
mov ah, 4Ah
int 21h
pop es
jc errMemRel
; Get SDA address
mov ax, 5D06h
int 21h ; Address returned in DS:SI
ASSUME ds:NOTHING
jc errNoSDA
mov [WORD HIGH SDA], ds
mov [WORD LOW SDA], si
mov [WORD HIGH pCurrCDS], ds
add [WORD LOW pCurrCDS], si
mov [WORD HIGH pDTA], ds
add [WORD LOW pDTA], si
mov [WORD HIGH FN1], ds
add [WORD LOW FN1], si
mov [WORD HIGH AccMode], ds
add [WORD LOW AccMode], si
mov [WORD HIGH SrchAttr], ds
add [WORD LOW SrchAttr], si
mov [WORD HIGH ExtOpenMode], ds
add [WORD LOW ExtOpenMode], si
mov ax, DSeg
mov ds, ax
ASSUME ds:DSeg
; Get address of LoL
mov ah, 52h
int 21h ; ES:BX -> List of Lists
ASSUME es:NOTHING
mov [WORD LOW LstOfLst], bx
mov [WORD HIGH LstOfLst], es
; Get Lastdrive
mov cl, [es:bx+21h]
mov [LastDrive], cl
; Check if XMS is present, and get driver entry point
mov ax, 4300h
int 2Fh
cmp al, 80h
jne NoXMS
mov [XMSFound], 1
mov ax, 4310h
int 2Fh
mov [WORD LOW XMSEntry], bx
mov [WORD HIGH XMSEntry], es
NoXMS:
; Parse command line
call ParseCmdLine
cmp [UnInstall], 1
jne InstallDriver
; Scan through INT 2D functions 00h-0FFh in descending order.
mov [ApiFunc], 0FFh
CallMultiplex:
mov ah, [ApiFunc]
xor al, al ; Installation check
int 2Dh
cmp al, 0FFh
jne @@NextMultiplex
mov bx, ResCode ; Compare signature strings
mov si, OFFSET AMISSign
push ds
mov ds, bx
mov es, dx ; ES=driver's ResCode segment
mov cx, 4
cld
repe cmpsd
pop ds
jne @@NextMultiplex
; Installed iHPFS found.
mov [DriverLoaded], 1
cmp [SpecUninstall], 0
jz @@TotalUninstall ; Uninstall all drives
; Loop through list of drives to uninstall.
mov bx, -1
@@UninstallDrives:
inc bx
cmp bx, 26
je @@NextMultiplex
cmp [UninstallDrv+bx], 0
je @@UninstallDrives
movzx ax, [ApiFunc]
call NEAR QueryDrive, bx, ax
or ah, ah
jz @@UninstallDrives
mov [UninstallDrv+bx], 0 ; Driver found
call NEAR RemoveDrv, bx, es
or ah, ah
jnz @@errRmDrvFail
mov dl, bl
add dl, 'A'
mov [MsgDrvRemovedLetter], dl
mov ah, 9
mov dx, OFFSET MsgDrvRemoved
int 21h
jmp @@UninstallDrives
@@errRmDrvFail:
mov dl, bl
add dl, 'A'
mov [MsgCantRmDrvLetter], dl
mov dx, OFFSET MsgCantRmDrv
mov ah, 9
int 21h
jmp @@UninstallDrives
@@TotalUninstall:
call NEAR UninstallDriver, es
mov [Installed], 1 ; Driver uninstalled
jmp @@NextMultiplex
@@NextMultiplex:
sub [ApiFunc], 1
jnc CallMultiplex
cmp [DriverLoaded], 0
je @@errNotLoaded
cmp [SpecUninstall], 0
jnz @@ChkUninstallResult
cmp [Installed], 0
je @@errCantUninstall
; Total uninstall done.
mov ah, 9
mov dx, OFFSET MsgUnInstalled
int 21h
mov ax, 4C00h
int 21h
; Check if any drives couldn't be uninstalled.
@@ChkUninstallResult:
mov bx, -1
@@ChkUninstall1:
inc bx
cmp bx, 26
je @@Exit
cmp [UninstallDrv+bx], 0
je @@ChkUninstall1
mov dl, bl
add dl, 'A'
mov [MsgDrvNotInstLetter], dl
mov dx, OFFSET MsgDrvNotInst
mov ah, 9
int 21h
jmp @@ChkUninstall1
InstallDriver:
ASSUME cs:CSeg,ds:DSeg,es:NOTHING,fs:NOTHING,ss:SSeg,gs:ResCode
; Release environment block.
mov es, [PSP]
mov ax, [es:2Ch]
mov es, ax
mov ah, 49h
int 21h
jc errEnvRel
; Find an unused Multiplex function
xor ah, ah
FindFreeMux:
xor al, al ; Installation check
push ax
int 2Dh
or al, al
pop ax
je FoundFreeMux
add ah, 1
jz errNoFreeMux
jmp FindFreeMux
FoundFreeMux:
mov [ApiFunc], ah ; Save Multiplex function
call NEAR ScanDisks
; Check if any specified partitions weren't found.
mov bx, -1
@@ChkPartFound:
inc bx
cmp bx, 26
je @@ChkPartFound1
cmp [Partitions+bx], 0FEh
jae @@ChkPartFound
mov ax, bx
mov dl, 10
div dl
or al, al
jz @@ChkPartFound2
add al, 30h
@@ChkPartFound2:
add ah, 30h
mov [WORD MsgPartNotFoundNr], ax
mov dx, OFFSET MsgPartNotFound
mov ah, 9
int 21h
mov [ErrSignaled], 1
jmp @@ChkPartFound
@@ChkPartFound1:
cmp [Installed], 0
jnz @@Installed ; One or more drives installed
cmp [ErrSignaled], 0
jnz @@Exit ; Error message already displayed
jmp errNoHPFS ; No HPFS partitions found
; One or more drives successfully installed.
@@Installed:
mov ax, ResCode
mov es, ax
ASSUME es:ResCode
; Get File Character Upcase Table
mov ax, 6504h
mov bx, 0FFFFh
mov cx, 5
mov dx, 0FFFFh
mov di, OFFSET TermChars
int 21h
jc @@GetFileChar
push ds
lds si, [DWORD TermChars+1]
add si, 2
mov di, OFFSET UpCaseTbl
mov cx, 32
cld
rep movsd
pop ds
; Get File-Character Table
@@GetFileChar:
mov ax, 6505h ; Get File-Character Table
mov bx, 0FFFFh ; Default codepage
mov cx, 5 ; Buffer size
mov dx, 0FFFFh ; Default country ID
mov di, OFFSET TermChars
int 21h
jc @@FileChar2
mov si, [WORD TermChars+1]
mov ax, [WORD TermChars+3]
mov ds, ax
ASSUME ds:NOTHING
add si, 3
cld
lodsw
mov [WORD MinPerm], ax
inc si
lodsw
mov [WORD MinExcl], ax
inc si
lodsb
mov cl, al
mov [NumTerm], al
xor ch, ch
mov di, OFFSET TermChars
rep movsb
@@FileChar2:
; Get interrupt 2D and 2F vectors.
ASSUME ds:NOTHING,es:NOTHING,fs:ResCode,gs:NOTHING
mov ax, ResCode
mov fs, ax
GetIntVectors:
mov ax, 352Dh
int 21h
mov [WORD OldInt2D], bx
mov [WORD HIGH OldInt2D], es
mov ax, 352Fh
int 21h
mov [WORD OldInt2F], bx
mov [WORD HIGH OldInt2F], es
; Set new interrupt 2D and 2F vectors.
push ds
mov ax, ResCode
mov ds, ax
mov ax, 252Dh
mov dx, OFFSET Int2DEntry
int 21h
mov ax, 252Fh
mov dx, OFFSET Int2FEntry
int 21h
pop ds
; Terminate and Stay Resident
mov ax, 3100h
mov dx, ResData
sub dx, ResCode
add dx, 16 ; 16 paras for PSP
mov bx, OFFSET EndResData
shr bx, 4
add dx, bx
inc dx
int 21h
@@Exit:
mov ax, 4C00h
int 21h
;--------
; Errors
errBadDosVer:
Abort 0
errEnvRel:
errMemRel:
Abort 1 ; Malloc error
errNoSDA:
Abort 3 ; Compatibility error
errNoHPFS:
Abort 5
errNoFreeMux:
Abort 14 ; No free multiplex function
@@errCantUninstall:
Abort 11 ; Cannot unload
@@errNotLoaded:
Abort 15 ; Driver not loaded
ENDP Main
;---------------------------------------------------------------------
; Scans all hard disks in the system.
PROC ScanDisks STDCALL
ASSUME ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
LOCAL @@Drive:BYTE, @@Cyls:WORD, @@Heads:WORD, @@Secs:WORD
LOCAL @@IBM_MS_Ext:WORD
push es fs gs
mov ax, ResCode
mov es, ax
mov fs, ax
mov [@@Drive], 80h
@@DoDrive:
; Get drive type
mov ah, 15h
mov dl, [@@Drive]
int 13h
jc @@Done
cmp ah, 03h
jne @@Done
; Get drive parameters
mov ah, 08h
mov dl, [@@Drive]
int 13h
jc @@Done
mov ax, cx
xchg ah, al
shr ah, 6
inc ax
mov [@@Cyls], ax
mov [BYTE LOW @@Heads], dh
mov [BYTE HIGH @@Heads], 0
inc [@@Heads]
and cx, 3Fh
mov [@@Secs], cx
; Check for IBM/MS Extensions
mov [@@IBM_MS_Ext], 0
mov ah, 41h
mov bx, 55AAh
mov dl, [@@Drive]
int 13h
jc @@ExtChecked
cmp bx, 0AA55h
jne @@ExtChecked
test cl, 1 ; Extended functions supported
jne @@ExtChecked
mov [@@IBM_MS_Ext], 1
@@ExtChecked:
; Read the first sector just to make sure the drive exists. This is the only
; safe way as the drive type/drive param calls may return crazy values.
mov ax, 0201h
mov bx, OFFSET Buf1
mov cx, 1
movzx dx, [@@Drive]
int 13h
jc @@Done
movzx dx, [@@Drive]
mov [ExtPartBase], 0
call NEAR ScanPartTbl, Method_CHS,dx,[@@Heads],[@@Secs],[@@IBM_MS_Ext],0,0,1,0 0
inc [@@Drive]
jnc @@DoDrive
@@Done:
pop gs fs es
ret
ENDP ScanDisks
;---------------------------------------------------------------------
; Scans partition tables on a drive.
; Args: @@Method Disk access method to use
; @@Drive Drive number
; @@nHeads Number of heads (logical)
; @@nSecs Number of sectors (logical)
; @@IBM_MS_Ext IBM/MS Extensions supported
; @@PartCyl Cylinder of partition table
; @@PartHead Head of partition table
; @@PartSec Sector of partition table
; @@PartLBA LBA of partition table
; Return CF=1 if a critical error is encountered (scan should not continue)
PROC ScanPartTbl PASCAL
ASSUME ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
ARG @@Method,@@Drive,@@nHeads,@@nSecs,@@IBM_MS_Ext,@@PartCyl,@@PartHead,@@PartSec,@@PartLBA:DWORD
LOCAL @@PartOfs:WORD, @@HighPart:BYTE, @@sLBA:DWORD
LOCAL @@sCyl:WORD, @@sHead:WORD, @@sSec:WORD
LOCAL @@eCyl:WORD, @@eHead:WORD, @@eSec:WORD
mov [@@PartOfs], 1BEh
@@DoEntry:
mov [@@HighPart], 0
cmp [@@Method], Method_CHS
je @@ReadPartCHS
cmp [@@Method], Method_CHSExt
je @@ReadPartCHSExt
jmp @@ReadPartExt
; Read partition table using CHS
@@ReadPartCHS:
mov ax, 0201h
mov bx, OFFSET Buf1
mov ch, [BYTE LOW @@PartCyl]
mov cl, [BYTE HIGH @@PartCyl]
shl cl, 6
xor cl, [BYTE LOW @@PartSec]
mov dh, [BYTE LOW @@PartHead]
mov dl, [BYTE LOW @@Drive]
int 13h
jc @@errReadError
jmp @@PartTableRead
; Read partition table using extended CHS
@@ReadPartCHSExt:
call NEAR ReadSectorExtCHS PASCAL, [@@PartLBA],[@@Drive],[@@nHeads],[@@nSecs],es (OFFSET Buf1)
jc @@errReadError
jmp @@PartTableRead
; Read partition table using IBM/MS Extensions
@@ReadPartExt:
mov [DiskAddrPkt1.Count], 1
mov eax, [@@PartLBA]
mov [DWORD LOW DiskAddrPkt1.Sector], eax
mov [WORD LOW DiskAddrPkt1.Buffer], OFFSET Buf1
mov [WORD HIGH DiskAddrPkt1.Buffer], SEG Buf1
mov dl, [BYTE LOW @@Drive]
mov si, OFFSET DiskAddrPkt1
mov ah, 42h
int 13h
jc @@errReadError
@@PartTableRead:
; Check partition table signature
cmp [WORD Buf1+510], 0AA55h
jne @@errBadPartTable
mov bx, [@@PartOfs]
cmp [BYTE Buf1+bx+04h], 0 ; Unused entry
jz @@NextEntry
; Extract CHS information
movzx ax, [Buf1+bx+01h]
mov [@@sHead], ax
movzx ax, [Buf1+bx+02h]
mov [@@sSec], ax
and [@@sSec], 3Fh
shl ax, 2
mov al, [Buf1+bx+03h]
mov [@@sCyl], ax
movzx ax, [Buf1+bx+05h]
mov [@@eHead], ax
movzx ax, [Buf1+bx+06h]
mov [@@eSec], ax
and [@@eSec], 3Fh
shl ax, 2
mov al, [Buf1+bx+07h]
mov [@@eCyl], ax
; Calculate partition size from CHS information to determine if high
movzx eax, [@@eCyl]
sub ax, [@@sCyl]
mul [@@nHeads]
shl edx, 16
add eax, edx
movzx edx, [@@nSecs]
mul edx
mov ecx, eax
movzx eax, [BYTE LOW @@eHead]
sub al, [BYTE LOW @@sHead]
mul [BYTE LOW @@nSecs]
add al, [BYTE LOW @@eSec]
adc ah, 0
sub al, [BYTE LOW @@sSec]
sbb ah, 0
add ecx, eax
inc ecx ; ECX = partition size
cmp ecx, [DWORD Buf1+bx+0Ch]
je @@PartSizeDone
mov [@@HighPart], 1
cmp ecx, 1
je @@PartSizeDone
cmp [Buf1+bx+04h], 05h
jne @@PartSizeDone
mov [@@HighPart], 0 ; Extended partition, partially high
@@PartSizeDone:
mov al, [Buf1+bx+04h] ; Partition type
cmp al, 05h
je @@ExtPart
cmp al, 07h
je @@HPFSPart
cmp al, 17h
je @@HPFSPart
jmp @@NextEntry
@@ExtPart:
mov eax, [DWORD Buf1+bx+08h]
add eax, [ExtPartBase] ; Relative to first extended partition
mov [@@sLBA], eax ; LBA of partition
cmp [ExtPartBase], 0
jnz @@ExtBaseDone
mov [ExtPartBase], eax ; This is the first ext part
@@ExtBaseDone:
cmp [@@HighPart], 0
jnz @@ExtPartHigh
call NEAR ScanPartTbl, Method_CHS,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],[@@sCyl],[@@sHead],[@@sSec],[@@sLBA]
jc @@Done
jmp @@NextEntry
@@ExtPartHigh:
cmp [@@IBM_MS_Ext], 0
jnz @@ExtPartIBMExt
cmp [UseExtCHS], 0
jz @@NextEntry
test [BYTE LOW @@nHeads], 0C0h
jnz @@NextEntry ; Must have at most 63 heads
call NEAR CheckCylNumber, [@@sLBA], [@@nHeads], [@@nSecs]
jnz @@NextEntry
call NEAR ScanPartTbl, Method_CHSExt,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],0,0,0,[@@sLBA]
jc @@Done
jmp @@NextEntry
@@ExtPartIBMExt:
call NEAR ScanPartTbl, Method_Ext,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],0,0,0,[@@sLBA]
jc @@Done
jmp @@NextEntry
@@HPFSPart:
mov eax, [@@PartLBA] ; Relative to current ext part
add eax, [DWORD Buf1+bx+08h]
mov [@@sLBA], eax ; LBA of partition
cmp [@@HighPart], 0
jnz @@HPFSPartHigh
call NEAR CheckHPFSPart, Method_CHS,[@@Drive],[@@nHeads],[@@nSecs],[@@sCyl],[@@sHead],[@@sSec],[@@sLBA]
jc @@Done
jmp @@NextEntry
@@HPFSPartHigh:
cmp [@@IBM_MS_Ext], 0
jnz @@HPFSPartIBMExt
cmp [UseExtCHS], 0
jz @@NextEntry
test [BYTE LOW @@nHeads], 0C0h
jnz @@NextEntry ; Must have at most 63 heads
mov eax, [@@sLBA] ; Check that we can read last sector
add eax, [DWORD Buf1+bx+0Ch]
dec eax
call NEAR CheckCylNumber, eax, [@@nHeads], [@@nSecs]
jnz @@NextEntry
call NEAR CheckHPFSPart, Method_CHSExt,[@@Drive],[@@nHeads],[@@nSecs],0,0,0,[@@sLBA]
jc @@Done
jmp @@NextEntry
@@HPFSPartIBMExt:
call NEAR CheckHPFSPart, Method_Ext,[@@Drive],[@@nHeads],[@@nSecs],0,0,0,[@@sLBA]
jc @@Done
jmp @@NextEntry
@@NextEntry:
add [@@PartOfs], 10h
cmp [@@PartOfs], 1FEh
jb @@DoEntry
clc
jmp @@Done
@@errReadError:
mov dx, OFFSET MsgDiskError
mov ah, 9
int 21h
stc
jmp @@Done
@@errBadPartTable:
mov dx, OFFSET MsgBadPartTable
mov ah, 9
int 21h
stc
; FALL THROUGH to @@Done
@@Done:
ret
ENDP ScanPartTbl
;---------------------------------------------------------------------
; Checks and possibly installs an HPFS partition. Uses Buf2
; Args: @@Method Disk access method to use
; @@PhysDrive Drive number
; @@nHeads Number of heads (logical)
; @@nSecs Number of sectors (logical)
; @@sCyl Starting cylinder
; @@sHead Starting head
; @@sSec Starting sector
; @@sLBA Starting LBA
; Returns CF=1 if a critical error occurs.
PROC CheckHPFSPart PASCAL
ASSUME ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
ARG @@Method,@@PhysDrive,@@nHeads,@@nSecs,@@sCyl,@@sHead,@@sSec,@@sLBA:DWORD
LOCAL @@Drive:BYTE
push ds es fs gs
pushad
cmp [@@Method], Method_CHS
je @@ReadCHS
cmp [@@Method], Method_CHSExt
je @@ReadCHSExt
jmp @@ReadExt
; Read boot sector using CHS
@@ReadCHS:
mov ax, 0201h
mov bx, OFFSET Buf2
mov ch, [BYTE LOW @@sCyl]
mov cl, [BYTE HIGH @@sCyl]
shl cl, 6
xor cl, [BYTE LOW @@sSec]
mov dh, [BYTE LOW @@sHead]
mov dl, [BYTE LOW @@PhysDrive]
int 13h
jc @@errReadError
jmp @@BootRead
; Read boot sector using extended CHS
@@ReadCHSExt:
call NEAR ReadSectorExtCHS PASCAL, [@@sLBA],[@@PhysDrive],[@@nHeads],[@@nSecs],es (OFFSET Buf2)
jc @@errReadError
jmp @@BootRead
; Read boot sector using IBM/MS Extensions
@@ReadExt:
mov [DiskAddrPkt1.Count], 1
mov eax, [@@sLBA]
mov [DWORD LOW DiskAddrPkt1.Sector], eax
mov [WORD LOW DiskAddrPkt1.Buffer], OFFSET Buf2
mov [WORD HIGH DiskAddrPkt1.Buffer], SEG Buf2
mov dl, [BYTE LOW @@PhysDrive]
mov si, OFFSET DiskAddrPkt1
mov ah, 42h
int 13h
jc @@errReadError
@@BootRead:
; Check the HPFS signature
cmp [WORD Buf2+36h], 'PH'
jne @@Done
cmp [WORD Buf2+38h], 'SF'
jne @@Done
; HPFS partition found.
inc [PartCount]
movzx bx, [PartCount]
mov dl, [Partitions+bx]
mov [@@Drive], dl
mov [Partitions+bx], 0FFh ; Partition found
cmp dl, 0FFh
je @@Done ; Don't install this partition
; Check that partition is not already installed
call NEAR IsInstalledPart, bx
or ah, ah
jnz @@errPartInstalled
les bx, [LstOfLst]
ASSUME es:NOTHING
les si, [es:bx+16h] ; CDS array
cmp [@@Drive], 0FEh
je @@ScanCDS ; Find first free drive letter
mov ah, 36h ; Get disk free space
mov dl, [@@Drive]
inc dl
int 21h
cmp ax, 0FFFFh
jne @@errDrvUsed
mov al, [BYTE CDSSize]
mov cl, [@@Drive]
mul cl
mov si, ax ; Points to CDS for our drive
cmp cl, [LastDrive]
jb @@FoundCDS
jmp @@errInvDrv
; Search the CDS array, look for an unused CDS.
; ES:SI -> CDS.
@@ScanCDS:
mov [@@Drive], 0
@@ScanCDS1:
cmp [DrDos], 0
jz @@ScanCDS2
cmp [WORD es:si+43h], 0 ; DR-DOS
jmp @@ScanCDS3
@@ScanCDS2:
test [WORD es:si+43h], 0C000h ; Mask bits 15 and 14 of drv attributes
@@ScanCDS3:
jz @@FoundCDS ; If 0, then drive is invalid = free
add si, [CDSSize] ; Point to next CDS
inc [@@Drive]
mov cl, [@@Drive]
cmp cl, [LastDrive]
jb @@ScanCDS1 ; Go to next CDS entry
jmp @@errOutOfDrv
@@FoundCDS:
; Allocate memory for resident data
mov bx, OFFSET EndResData
shr bx, 4
inc bx
mov ah, 48h
int 21h
jc @@errMallocErr
movzx bx, [@@Drive]
shl bx, 1
mov [DataSegs+bx], ax ; Save ResData segment
mov gs, ax
ASSUME gs:ResData
; Clear resident data.
push es
mov es, ax
xor al, al
cld
mov cx, OFFSET EndResData
xor di, di
rep stosb
pop es
; Set resident data
mov al, [BYTE LOW @@PhysDrive]
mov [PhysDrv], al
mov ax, [@@nHeads]
mov [nHeads], ax
mov al, [BYTE LOW @@nSecs]
mov [nSecs], al
mov al, [PartCount]
mov [PartitionNr], al
mov al, [BYTE LOW @@Method]
mov [AccessMethod], al
mov eax, [@@sLBA]
mov [LBAstart], eax
call InitResData
; Set CDS fields
mov cl, [@@Drive]
add cl, 'A'
mov [MsgDrvLetter], cl
cmp [DrDos], 0
jz @@SetCDS
mov [WORD es:si+43h], 8000h ; DR-DOS
jmp @@CDSSet
@@SetCDS:
or [WORD es:si+43h], 0C000h ; Flags+Physical bits on = Netwrk drive
@@CDSSet:
mov [es:si], cl
mov [WORD es:si+1], '\:'
mov [BYTE es:si+3], 0
mov [WORD es:si+4Fh], 2 ; Offset of backslash
mov [Installed], 1 ; Drive successfully installed
; Partition installed - print message
mov dx, OFFSET MsgInstalled
mov ah, 9
int 21h
jmp @@Done
@@errReadError:
mov dx, OFFSET MsgDiskError
mov ah, 9
int 21h
jmp @@Done
@@errDrvUsed:
mov dl, [@@Drive]
add dl, 'A'
mov [MsgDrvUsedLetter], dl
mov dx, OFFSET MsgDrvUsed
mov ah, 9
int 21h
mov [ErrSignaled], 1
jmp @@Done
@@errInvDrv:
mov dl, [@@Drive]
add dl, 'A'
mov [MsgInvDrvLetter], dl
mov dx, OFFSET MsgInvDrv
mov ah, 9
int 21h
mov [ErrSignaled], 1
jmp @@Done
@@errPartInstalled:
movzx ax, [PartCount]
mov dl, 10
div dl
or al, al
jz @@errPartInstalled1 ; Leave 00h if first digit 0.
add al, 30h
@@errPartInstalled1:
add ah, 30h
mov [WORD MsgPartInstalledNr], ax
mov dx, OFFSET MsgPartInstalled
mov ah, 9
int 21h
mov [ErrSignaled], 1
jmp @@Done
@@errOutOfDrv:
mov dx, OFFSET MsgNoAvailDrvLetter
mov ah, 9
int 21h
mov [ErrSignaled], 1
jmp @@Fail
@@errMallocErr:
mov dx, OFFSET MsgMallocErr
mov ah, 9
int 21h
mov [ErrSignaled], 1
jmp @@Fail
@@Fail: stc
jmp @@Exit
@@Done:
clc
@@Exit:
popad
pop gs fs es ds
ret
ENDP CheckHPFSPart
;---------------------------------------------------------------------
; Read a sector given by LBA using extended CHS addressing.
PROC ReadSectorExtCHS PASCAL
ASSUME ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
ARG @@Sector:DWORD, @@Drive:WORD, @@nHeads:WORD, @@nSecs:WORD, @@Buf:DWORD
pushad
push es
mov ecx, [@@Sector]
mov ax, [@@nSecs]
mul [@@nHeads]
mov bx, ax ; Sectors/track * heads
mov ax, cx
mov edx, ecx
shr edx, 16 ; DX:AX = logical sector #
div bx
push ax ; Cylinder
mov ax, dx
div [BYTE LOW @@nSecs]
movzx dx, al ; Head
mov cl, ah ; Sector
inc cl
pop ax ; Cylinder
mov dh, dl ; Head
mov ch, al
xor al, al
shr ax, 2
or cl, al ; bits 8 and 9 of cyl. number go here
xor al, al
shr ax, 2
or dh, al ; bits 10 and 11 of cyl (BIOS extension)
mov ax, 0201h
les bx, [@@Buf]
mov dl, [BYTE LOW @@Drive]
int 13h
pop es
popad
ret
ENDP ReadSectorExtCHS
;---------------------------------------------------------------------
; Determines the cylinder number of a given logical sector and returns
; ZF=1 if it is less than 4096, which means it can be read with
; extended CHS addressing.
PROC CheckCylNumber PASCAL
ASSUME ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
ARG @@Sector:DWORD, @@nHeads:WORD, @@nSecs:WORD
pushad
mov ecx, [@@Sector]
mov ax, [@@nSecs]
mul [@@nHeads]
mov bx, ax ; Sectors/track * heads
mov ax, cx
mov edx, ecx
shr edx, 16 ; DX:AX = logical sector #
div bx
test ax, 0F000h
popad
ret
ENDP CheckCylNumber
;---------------------------------------------------------------------
; Initialize resident data. Assumes GS=ResData
PROC InitResData STDCALL
ASSUME ds:ResCode,es:ResData,fs:NOTHING,gs:ResData
LOCAL @@BitmapTable:DWORD
pushad
push ds es fs gs
mov ax, ResCode
mov ds, ax
push gs
pop es
; Read the boot sector
call FAR ThunkReadSector PASCAL, LARGE 0, 1, ds (OFFSET Buf1)
jc @@ReadError
mov cx, 11
mov si, OFFSET Buf1+2Bh
mov di, OFFSET Volabel
rep movsb
mov al, [BYTE Buf1+15h] ; Media ID byte
mov [MediaID], al
; Read the SuperBlock
call FAR ThunkReadSector PASCAL, LARGE 16, 1, ds (OFFSET Buf1)
jc @@ReadError
mov eax, [DWORD Buf1+0Ch] ; Root dir fnode
mov [CDFNode], eax
mov [RootFNode], eax
mov eax, [DWORD Buf1+10h] ; Partition size in sectors
mov [TotalSectors], eax
mov eax, [DWORD Buf1+18h]
mov [@@BitmapTable], eax
; Scan the free space bitmaps and count free sectors.
mov edx, [TotalSectors]
add edx, 3FFFh
shr edx, 14 ; Number of bands
shl dx, 2
xor bx, bx ; offset into bitmap table
xor ecx, ecx ; bit count
@@DoBand:
; Read free space bitmap table
call FAR ThunkReadSector PASCAL, [@@BitmapTable], 4, ds (OFFSET Buf1)
jc @@ReadError
; Read free space bitmaps
call FAR ThunkReadSector PASCAL, [DWORD Buf1+bx], 4, ds (OFFSET Buf1)
jc @@ReadError
call CountBits
add ecx, eax
add bx, 4
cmp bx, dx
jne @@DoBand
mov [FreeSectors], ecx
pop gs fs es ds
popad
ret
@@ReadError:
Abort 4
ENDP InitResData
;---------------------------------------------------------------------
; Count number of set bits in Buf. Assumes DS=ResCode. Returns
; number of set bits in EAX.
PROC CountBits
ASSUME ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
push cx dx si
xor dx, dx
mov si, OFFSET Buf1
mov cx, 4*512
cld
@@Bytes:
lodsb
push cx
mov cx, 8
@@Bits: shr al, 1
adc dx, 0
loop @@Bits
pop cx
loop @@Bytes
movzx eax, dx
pop si dx cx
ret
ENDP CountBits
;---------------------------------------------------------------------
; Write error message BX, release memory and exit
PROC AbortMsg
ASSUME cs:CSeg,ds:DSeg,es:NOTHING,fs:ResCode,gs:NOTHING
push bx
mov ax, DSeg
mov ds, ax
mov ax, ResCode
mov fs, ax
shl bx, 1
mov dx, [ErrMsgTbl+bx]
mov ah, 9
int 21h
; Deallocate XMS
cmp [CacheOn], 0
jz @@Exit
mov cl, [XMSBlocks]
xor ch, ch
jcxz @@1
mov ah, 0Ah
mov dx, [hCacheSectors]
call [XMSEntry]
dec cx
jcxz @@1
mov ah, 0Ah
mov dx, [hCacheLists]
call [XMSEntry]
dec cx
jcxz @@1
mov ah, 0Ah
mov dx, [hHashTable]
call [XMSEntry]
; Release allocated ResData blocks.
@@1: xor bx, bx
mov cx, 26
@@2: mov ax, [DataSegs+bx]
or ax, ax
jz @@3
mov es, ax
mov ah, 49h
push bx cx
int 21h
pop cx bx
@@3: add bx, 2
loop @@2
@@Exit:
pop ax
mov ah, 4Ch
int 21h
ENDP AbortMsg
;---------------------------------------------------------------------
; Check if iHPFS is alredy loaded for a partition. Returns
; AH=00h if not installed, 01h if installed
; DL=drive number if partition installed.
PROC IsInstalledPart PASCAL
ARG @@Part
LOCAL @@Func:BYTE, @@Result, @@Drive:BYTE
pushad
; Scan through INT 2D functions 00h-0FFh.
mov [@@Func], 0
@@CallMultiplex:
mov ah, [@@Func]
xor al, al ; Installation check
int 2Dh
cmp al, 0FFh
jne @@NextMultiplex
mov bx, ResCode ; Compare signature strings
mov si, OFFSET AMISSign
push ds
mov ds, bx
mov es, dx
mov cx, 4
cld
repe cmpsd
pop ds
jne @@NextMultiplex
; Installed iHPFS found.
call NEAR QueryPart, [@@Part], es
mov [@@Result], ax
mov [@@Drive], dl
or ah, ah
jnz @@Done ; Partition found, exit.
@@NextMultiplex:
add [@@Func], 1
jnz @@CallMultiplex
mov [@@Result], 0FFh ; Partition not found.
mov [@@Drive], 0FFh
@@Done: popad
mov ax, [@@Result]
mov dl, [@@Drive]
ret
ENDP IsInstalledPart
;---------------------------------------------------------------------
; Uninstall driver.
PROC UninstallDriver PASCAL
ARG @@ResCode ; ResCode of driver to uninstall
LOCAL @@PSP
ASSUME ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
pushad
push ds es gs
mov ds, [@@ResCode]
; Release XMS memory
cmp [CacheOn], 0
jz @@XMSReleased
mov [CacheOn], 0
mov ah, 0Ah
mov dx, [hHashTable]
call [XMSEntry]
mov ah, 0Ah
mov dx, [hCacheLists]
call [XMSEntry]
mov ah, 0Ah
mov dx, [hCacheSectors]
call [XMSEntry]
@@XMSReleased:
; Disconnect drives
mov cx, -1
@@Remove:
inc cx
cmp cx, 26
je @@DrivesDisconnected
mov bx, cx
shl bx, 1
cmp [DataSegs+bx], 0
jz @@Remove
call NEAR RemoveDrv, cx, ds
jmp @@Remove
@@DrivesDisconnected:
; Get PSP.
mov ah, 51h
int 21h
mov [@@PSP], bx ; Save old PSP
; Set PSP to resident driver. This is so the memory block retains its old owner.
mov bx, [PSP]
mov ah, 50h
int 21h
mov es, bx
ASSUME es:NOTHING
; See if interrupt vectors have been hooked by another TSR
xor ax, ax
mov gs, ax
mov ax, [@@ResCode]
cmp [WORD LOW DWORD gs:2Dh*4], OFFSET Int2DEntry
jne @@Resize
cmp [WORD HIGH DWORD gs:2Dh*4], ax
jne @@Resize
cmp [WORD LOW DWORD gs:2Fh*4], OFFSET Int2FEntry
jne @@Resize
cmp [WORD HIGH DWORD gs:2Fh*4], ax
jne @@Resize
; Restore interrupt vectors
push ds
pop gs
ASSUME gs:ResCode
lds dx, [OldInt2D]
ASSUME ds:NOTHING
mov ax, 252Dh
int 21h
lds dx, [OldInt2F]
mov ax, 252Fh
int 21h
push gs
pop ds
ASSUME ds:ResCode
; Release memory block
mov ah, 49h
int 21h
jmp @@MemReleased
; Resize memory block
@@Resize:
mov bx, OFFSET EndUninstalledCode
shr bx, 4
add bx, 17 ; Paragraphs to keep (code+PSP+1)
mov ah, 4Ah
int 21h
; Patch in far jump into driver's interrupt code to chain to original handler.
mov [WORD Int2DEntry], 0EA90h ; NOP and JMP FAR
mov [WORD Int2FEntry], 0EA90h
@@MemReleased:
; Back to original PSP
mov bx, [@@PSP]
mov ah, 50h
int 21h
pop gs es ds
popad
mov al, 0FFh
ret
ENDP UninstallDriver
;---------------------------------------------------------------------
; Query logical drive connected.
; Return: AH=Install status (00h Not installed, 01h Installed)
PROC QueryDrive PASCAL
ARG @@Drive, @@ApiFunc
ASSUME ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
push bx
mov ah, [BYTE LOW @@ApiFunc]
mov al, 10h ; Query drive
mov bx, [@@Drive]
int 2Dh
pop bx
ret
ENDP QueryDrive
;---------------------------------------------------------------------
; Query HPFS partition connected.
; Return: AH=Install status (00h Not installed, 01h Installed)
; DL=Drive letter associated with partition (if installed)
PROC QueryPart PASCAL
ARG @@PartNum, @@ResCodeSeg
ASSUME ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
push bx cx dx
push ds es
mov ds, [@@ResCodeSeg]
mov dx, -1
@@1:
inc dx
cmp dx, 26
je @@PartNotFound
movzx bx, dl
shl bx, 1
mov bx, [DataSegs+bx]
or bx, bx
je @@1
mov es, bx
ASSUME es:ResData
mov cl, [BYTE LOW @@PartNum]
cmp [PartitionNr], cl
jne @@1
ASSUME es:NOTHING
mov ax, 01FFh
jmp @@Done
@@PartNotFound:
mov ax, 0FFh
@@Done:
pop es ds
pop dx cx bx
ret
ENDP QueryPart
;---------------------------------------------------------------------
; Remove a drive.
; Return: AH=result
; 00h = Uninstalled
; 01h = Drv not installed
; 02h = Failed for other reason
PROC RemoveDrv PASCAL
ARG @@Drive, @@ResCodeSeg
LOCAL @@RetValue
ASSUME ds:ResCode,es:NOTHING,fs:DSeg,gs:NOTHING
pushad
push ds es fs
mov ax, DSeg
mov fs, ax
mov ds, [@@ResCodeSeg]
; Release resident data segment
movzx bx, [BYTE LOW @@Drive]
shl bx, 1
mov dx, [DataSegs+bx]
or dx, dx
jz @@NotInstalled
mov [DataSegs+bx], 0
mov es, dx
mov ah, 49h
int 21h
; Patch CDS
mov ah, 52h
int 21h ; Get List of Lists in ES:BX
les bx, [es:bx+16h] ; CDS array
mov al, [BYTE LOW @@Drive]
mov dl, [BYTE CDSSize]
mul dl
add bx, ax ; CDS entry for drive
mov [WORD es:bx+43h], 0
mov [@@RetValue], 0FFh
jmp @@Exit
@@NotInstalled:
mov [@@RetValue], 01FFh
@@Exit: pop fs es ds
popad
mov ax, [@@RetValue]
ret
ENDP RemoveDrv
;---------------------------------------------------------------------
; Parse command line.
PROC ParseCmdLine STDCALL
LOCAL @@LastByte, @@CacheOpt:BYTE, @@DrvSpecd, @@DriveNo:BYTE
LOCAL @@PartNum:BYTE, @@UninstallOpt:BYTE
ASSUME ds:DSeg,es:NOTHING,fs:ResCode
mov [@@CacheOpt], 0 ; /C option flag
mov [@@UninstallOpt], 0 ; /U option flag
mov [@@DrvSpecd], 0
mov es, [PSP]
movzx ax, [BYTE es:80h]
cmp al, 2
jb @@Done
add al, 80h
mov [@@LastByte], ax
; Find first non-space character
mov di, 81h
@@Next:
mov cx, [@@LastByte]
sub cx, di
jc @@Done
inc cx
mov al, ' '
cmp al, ' ' ; Set zero flag
repe scasb
je @@Done
dec di
; Get char and convert to upper case
mov dl, [es:di]
mov ax, 6520h
int 21h
; Check for drive spec 'A'..'Z'
cmp dl, 'A'
jb @@Parse1
cmp dl, 'Z'
ja @@Parse1
; Drive spec.
inc di
mov al, ':' ; Match a colon
scasb
jne @@errBadOption
sub dl, 'A' ; Drive number
mov [@@DriveNo], dl
; Check for partition number '1'..'9'
cmp di, [@@LastByte]
ja @@SetDrvLetter ; Save drive letter in list
mov al, [es:di]
cmp al, ' '
je @@SetDrvLetter
cmp al, '1'
jb @@errBadOption
cmp al, '9'
ja @@errBadOption
; Partition number
mov [Install], 1 ; Partition number - installing
inc di
sub al, '0'
mov [@@PartNum], al
cmp di, [@@LastByte]
ja @@SetPart
mov al, [es:di] ; Second digit
cmp al, ' '
je @@SetPart
cmp al, '0'
jb @@errBadOption
cmp al, '9'
ja @@errBadOption
inc di
sub al, '0'
mov bl, [@@PartNum]
shl bl, 3 ; Multiply by 10
add bl, [@@PartNum]
add bl, [@@PartNum]
add al, bl
mov [@@PartNum], al
cmp di, [@@LastByte]
ja @@SetPart
mov al, [es:di]
cmp al, ' '
jne @@errBadOption
@@SetPart:
mov [@@DrvSpecd], 1 ; Drive letter specified
movzx bx, [@@PartNum]
mov al, [@@DriveNo]
cmp [Partitions+bx], 0FEh
jne @@errPartUsed
mov [Partitions+bx], al
jmp @@Next
@@SetDrvLetter:
movzx bx, [@@DriveNo]
mov [UnInstallDrv+bx], 1 ; Uninstall this drive
mov [UnInstall], 1
mov [SpecUninstall], 1 ; Drive to uninstall specified
jmp @@Next
@@Parse1:
; Check for switches
mov al, '/'
scasb
jne @@errBadOption
; Switch
cmp di, [@@LastByte]
ja @@errBadOption
mov dl, [es:di]
inc di
mov ax, 6520h ; Upper case
int 21h
cmp dl, 'B'
je @@BIOSExtension
cmp dl, 'C'
je @@CacheSize
cmp dl, 'U'
je @@UnInstall
cmp dl, 'L'
je @@ConvertLong
cmp dl, 'M'
je @@Multitrack
jmp @@errBadOption
; /B switch
@@BIOSExtension:
mov [UseExtCHS], 1
cmp di, [@@LastByte]
ja @@Next
cmp [BYTE es:di], ' '
jne @@errBadOption
jmp @@Next
; /C switch
@@CacheSize:
cmp [@@CacheOpt], 0
jnz @@errBadOption ; Only one /C option allowed
mov [@@CacheOpt], 1
mov [Install], 1 ; /C switch - installing
cmp di, [@@LastByte]
je @@errBadOption
mov al, '='
scasb
jne @@errBadOption
xor eax, eax ; AX=Converted number
@@GetDigit:
cmp di, [@@LastByte]
ja @@ConvDone
cmp [BYTE es:di], ' '
je @@ConvDone
mov bx, 10
mul bx
or dx, dx
jne @@errBadOption
cmp [BYTE es:di], '0'
jb @@errBadOption
cmp [BYTE es:di], '9'
ja @@errBadOption
add al, [es:di]
adc ah, 0
jc @@errBadOption
sub ax, 30h
inc di
jmp @@GetDigit
@@ConvDone:
cmp ax, MinCacheSize ; Check that cache size is ok.
jb @@errBadOption
cmp ax, MaxCacheSize
ja @@errBadOption
mov ecx, eax
shl eax, 10 ; Bytes
mov edx, eax
shr edx, 16 ; Cache size in bytes in DX:AX
mov bx, 512+CacheEntrySize+2
div bx ; Cache entries
mov [CacheEntries], ax
mov [FreeEntry], ax
dec [FreeEntry]
xor dx, dx
mov bx, LoadFactor
div bx ; Hash table size=entries/load factor
mov [HashSize], ax
; Allocate the XMS memory blocks
cmp [XMSFound], 0
je @@errXMSFailure
mov dx, [CacheEntries]
shr dx, 1
adc dx, 0 ; KB needed for the sectors
mov ah, 09h
call [XMSEntry]
or ax, ax
jz @@errAllocFailed
mov [hCacheSectors], dx
inc [XMSBlocks]
mov ax, [CacheEntries]
mov dx, CacheEntrySize
mul dx
shl edx, 16
mov dx, ax
shr edx, 10
inc dx
mov ah, 09h
call [XMSEntry]
or ax, ax
jz @@errAllocFailed
mov [hCacheLists], dx
inc [XMSBlocks]
mov dx, [HashSize]
shr dx, 9
inc dx ; KB needed for hash table
mov ah, 09h
call [XMSEntry]
or ax, ax
jz @@errAllocFailed
mov [hHashTable], dx
inc [XMSBlocks]
mov [CacheOn], 1
; Clear the hash table
push ds es
push di
mov ax, fs
mov ds, ax
mov es, ax
ASSUME ds:ResCode,es:ResCode
mov di, OFFSET Buf1
mov cx, 100h
xor eax, eax
rep stosd ; Clear Buf1-Buf2
mov cx, [HashSize]
shr cx, 9
inc cx
mov [XMoveStruc.Length], 1024
mov [XMoveStruc.SourceHandle], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET Buf1
mov [WORD (XMoveStruc.SourceOffset)+2], ds
mov ax, [hHashTable]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.DestOffset], 0
@@InitCacheTbl:
mov ah, 0Bh
mov si, OFFSET XMoveStruc
call [XMSEntry]
add [XMoveStruc.DestOffset], 1024
or ax, ax
loopnz @@InitCacheTbl
jz @@errXMSFailure
; Clear the sentinel, entry 0
mov ax, [hCacheLists]
mov [XMoveStruc.DestHandle], ax
mov [XMoveStruc.DestOffset], 0
mov [WORD XMoveStruc.SourceOffset], OFFSET Buf1
mov [XMoveStruc.Length], CacheEntrySize
mov ah, 0Bh
call [XMSEntry]
jz @@errXMSFailure
pop di
pop es ds
ASSUME ds:DSeg,es:NOTHING
jmp @@Next
; /U switch
@@UnInstall:
mov [UnInstall], 1 ; /U switch - uninstall
mov [@@UnInstallOpt], 1
cmp di, [@@LastByte]
ja @@Next
cmp [BYTE es:di], ' '
jne @@errBadOption
jmp @@Next
; /L switch
@@ConvertLong:
mov [ConvertLong], 1
cmp di, [@@LastByte]
ja @@Next
cmp [BYTE es:di], ' '
jne @@errBadOption
jmp @@Next
@@Multitrack:
mov [Multitrack], 0
cmp di, [@@LastByte]
ja @@Next
cmp [BYTE es:di], ' '
jne @@errBadOption
jmp @@Next
@@errBadOption:
Abort 7
@@errPartUsed:
Abort 17 ; Partition already specified once
@@errXMSFailure:
Abort 10
@@errAllocFailed:
cmp bl, 0A0h
jne @@errAlloc1
Abort 9 ; Out of XMS
@@errAlloc1:
cmp bl, 0A1h
jne @@errAlloc2
Abort 13 ; Out of XMS handles
@@errAlloc2:
Abort 10
@@Done:
mov al, [UnInstall]
xor al, [@@UnInstallOpt]
jnz @@errBadOption ; Can't specify "/U" xor "d:"
mov al, [Install]
test al, [UnInstall]
jnz @@errBadOption ; Can't both install and uninstall
or al, al
jz @@Exit
cmp [@@DrvSpecd], 0
jz @@Exit
; Mark unwanted partition numbers
mov bx, -1
@@MarkPart:
inc bx
cmp bx, 26
ja @@Exit
cmp [Partitions+bx], 0FEh
jne @@MarkPart
mov [Partitions+bx], 0FFh
jmp @@MarkPart
@@Exit:
ret
ENDP ParseCmdLine
ENDS CSeg
;--------------------------------------------- Data for transient section
SEGMENT DSeg
MsgHello DB "iHPFS An installable HPFS driver for DOS "
DB 'Version 1.24 97-06-01',10,13
DB "Copyright (C) 1993-1997, Marcus Better.",10,13,10,13,"$"
MsgInstalled DB "Installed as "
MsgDrvLetter DB 0, ":",10,13,"$"
MsgUnInstalled DB "Driver uninstalled.",10,13,"$"
MsgDrvRemoved DB "Removed drive "
MsgDrvRemovedLetter DB "A:",10,13,"$"
DiskNumber DB 80h ; Hard disk number
PartCount DB 0 ; Partition counter
Partitions DB 27 DUP(0FEh) ; Maps partitions to drive letters.
; 0FFh=don't install, 0FEh=first free drv letter.
XMSFound DB 0 ; Flag: XMS Found?
XMSBlocks DB 0 ; XMS blocks allocated
Install DB 0 ; Install iHPFS
UnInstall DB 0 ; Uninstall iHPFS. Error if both Install AND UnInstall
UnInstallDrv DB 26 DUP(0); Uninstall drive if set.
SpecUninstall DB 0 ; Set if specific drives are to be uninstalled.
DriverLoaded DB 0 ; Set if iHPFS loaded (during uninstall)
Installed DB 0 ; Set if any drive successfully installed,
; or if any uninstalled.
ErrSignaled DB 0 ; Error message displayed in ScanPartTbl
ExtPartBase DD 0 ; Base of extended partition offsets
UseExtCHS DB 0 ; Use extended CHS addressing
DiskAddrPkt1 DiskAddrPacketStruct <>
; DOS info
LstOfLst DD 0 ; Address of List of Lists
LastDrive DB 0
DrDos DB 0 ; Nonzero if DR-DOS (zero if Novell DOS)
CDSSize DW 58h ; Size of CDS entry
; Error message table
ErrMsgTbl DW MsgBadDOSVer ; Error 0
DW MsgMallocErr ; Error 1
DW MsgNoAvailDrvLetter ; Error 2
DW MsgNoSDA ; Error 3
DW MsgDiskError ; Error 4
DW MsgNoPart ; Error 5
DW MsgDrvUsed ; Error 6
DW MsgBadCmdLine ; Error 7
DW MsgInvDrv ; Error 8
DW MsgOutOfXMS ; Error 9
DW MsgXMSAllocFailed ; Error 10
DW MsgCantUninstall ; Error 11
DW MsgCantRmDrv ; Error 12
DW MsgOutOfXMSHandles ; Error 13
DW MsgNoMux ; Error 14
DW MsgNotLoaded ; Error 15
DW MsgDrvNotInst ; Error 16
DW MsgOneDrivePerPart ; Error 17
DW MsgPartNotFound ; Error 18
DW MsgPartInstalled ; Error 19
; Error messages
MsgBadDOSVer DB "Wrong DOS version.",10,13,"$"
MsgMallocErr DB "Memory allocation error.",10,13,"$"
MsgNoAvailDrvLetter DB "Out of drive letters.",10,13,"$"
MsgNoSDA DB "Compatibility error.",10,13,"$"
MsgDiskError DB "Error reading disk.",10,13,"$"
MsgNoPart DB "Cannot find HPFS partition.",10,13,"$"
MsgDrvUsed DB "Drive "
MsgDrvUsedLetter DB "A: already in use.",10,13,"$"
MsgInvDrv DB "Invalid drive letter "
MsgInvDrvLetter DB "A:",10,13,"$"
MsgBadCmdLine DB "Invalid command line arguments.",10,13
DB "Syntax: IHPFS [options] [d:n d:n ...]",10,13
DB " IHPFS /U [d:]",10,13
DB "where d is a drive letter and n is the number of the "
DB "HPFS partition.",10,13,10,13
DB "Options:",10,13
DB "/B Use more BIOS extensions to access high partitions.",10,13
DB "/C=x Allocate x KB for cache. Must be between 32 and 32768.",10,13
DB "/L Convert long filenames.",10,13
DB "/M Disable multitrack operations.",10,13
DB "/U Uninstall driver.",10,13
DB "$"
MsgOutOfXMS DB "Out of XMS memory.",10,13,"$"
MsgXMSAllocFailed DB "Cannot allocate XMS memory.",10,13,"$"
MsgCantUninstall DB "Cannot unload driver.",10,13,"$"
MsgCantRmDrv DB "Couldn't uninstall drive "
MsgCantRmDrvLetter DB "A:",10,13,"$"
MsgOutOfXMSHandles DB "Out of XMS handles.",10,13,"$"
MsgNoMux DB "No free multiplex function found.",10,13,"$"
MsgNotLoaded DB "iHPFS not loaded.",10,13,"$"
MsgDrvNotInst DB "iHPFS is not installed for drive "
MsgDrvNotInstLetter DB "A:",10,13,"$"
MsgOneDrivePerPart DB "Cannot install two iHPFS drives for the same partition.",10,13,"$"
MsgPartNotFound DB "Partition "
MsgPartNotFoundNr DB " 0 not found.",10,13,"$"
MsgPartInstalled DB "iHPFS already installed for partition "
MsgPartInstalledNr DB " 0.",10,13,"$"
MsgBadPartTable DB "Bad partition table signature.",10,13,"$"
ENDS DSeg
SEGMENT SSeg STACK
DB 128 DUP ('STACK---')
ENDS SSeg
END Main